LCOV - code coverage report
Current view: top level - gcc/analyzer - kf.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 96.6 % 1406 1358
Test Date: 2026-05-11 19:44:49 Functions: 96.6 % 148 143
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Handling for the known behavior of various specific functions.
       2              :    Copyright (C) 2020-2026 Free Software Foundation, Inc.
       3              :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       4              : 
       5              : This file is part of GCC.
       6              : 
       7              : GCC is free software; you can redistribute it and/or modify it
       8              : under the terms of the GNU General Public License as published by
       9              : the Free Software Foundation; either version 3, or (at your option)
      10              : any later version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but
      13              : WITHOUT ANY WARRANTY; without even the implied warranty of
      14              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15              : General Public License for more details.
      16              : 
      17              : You should have received a copy of the GNU General Public License
      18              : along with GCC; see the file COPYING3.  If not see
      19              : <http://www.gnu.org/licenses/>.  */
      20              : 
      21              : #include "analyzer/common.h"
      22              : 
      23              : #include "diagnostic.h"
      24              : 
      25              : #include "analyzer/analyzer-logging.h"
      26              : #include "analyzer/region-model.h"
      27              : #include "analyzer/call-details.h"
      28              : #include "analyzer/call-info.h"
      29              : 
      30              : #if ENABLE_ANALYZER
      31              : 
      32              : namespace ana {
      33              : 
      34              : /* Abstract subclass for describing undefined behavior of an API.  */
      35              : 
      36              : class undefined_function_behavior
      37              :   : public pending_diagnostic_subclass<undefined_function_behavior>
      38              : {
      39              : public:
      40           28 :   undefined_function_behavior (const call_details &cd)
      41           28 :   : m_call_stmt (cd.get_call_stmt ()),
      42           28 :     m_callee_fndecl (cd.get_fndecl_for_call ())
      43              :   {
      44           28 :     gcc_assert (m_callee_fndecl);
      45           28 :   }
      46              : 
      47          250 :   const char *get_kind () const final override
      48              :   {
      49          250 :     return "undefined_behavior";
      50              :   }
      51              : 
      52           28 :   bool operator== (const undefined_function_behavior &other) const
      53              :   {
      54           28 :     return (&m_call_stmt == &other.m_call_stmt
      55           28 :             && m_callee_fndecl == other.m_callee_fndecl);
      56              :   }
      57              : 
      58           28 :   bool terminate_path_p () const final override { return true; }
      59              : 
      60           76 :   tree get_callee_fndecl () const { return m_callee_fndecl; }
      61              : 
      62              : private:
      63              :   const gimple &m_call_stmt;
      64              :   tree m_callee_fndecl;
      65              : };
      66              : 
      67              : /* class pure_known_function_with_default_return : public known_function.  */
      68              : 
      69              : void
      70         2270 : pure_known_function_with_default_return::
      71              : impl_call_pre (const call_details &cd) const
      72              : {
      73         2270 :   cd.set_any_lhs_with_defaults ();
      74         2270 : }
      75              : 
      76              : /* Implementations of specific functions.  */
      77              : 
      78              : /* Handler for "alloca".  */
      79              : 
      80        10269 : class kf_alloca : public builtin_known_function
      81              : {
      82              : public:
      83         1326 :   bool matches_call_types_p (const call_details &cd) const final override
      84              :   {
      85         1326 :     return cd.num_args () == 1;
      86              :   }
      87         1574 :   enum built_in_function builtin_code () const final override
      88              :   {
      89         1574 :     return BUILT_IN_ALLOCA;
      90              :   }
      91              :   void impl_call_pre (const call_details &cd) const final override;
      92              : };
      93              : 
      94              : void
      95          527 : kf_alloca::impl_call_pre (const call_details &cd) const
      96              : {
      97          527 :   const svalue *size_sval = cd.get_arg_svalue (0);
      98              : 
      99          527 :   region_model *model = cd.get_model ();
     100          527 :   region_model_manager *mgr = cd.get_manager ();
     101              : 
     102          527 :   const region *new_reg
     103          527 :     = model->create_region_for_alloca (size_sval, cd.get_ctxt ());
     104          527 :   const svalue *ptr_sval
     105          527 :     = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     106          527 :   cd.maybe_set_lhs (ptr_sval);
     107          527 : }
     108              : 
     109              : /* Handler for __atomic_exchange.
     110              :    Although the user-facing documentation specifies it as having this
     111              :    signature:
     112              :      void __atomic_exchange (type *ptr, type *val, type *ret, int memorder)
     113              : 
     114              :    by the time the C/C++ frontends have acted on it, any calls that
     115              :    can't be mapped to a _N variation end up with this signature:
     116              : 
     117              :      void
     118              :      __atomic_exchange (size_t sz, void *ptr, void *val, void *ret,
     119              :                         int memorder)
     120              : 
     121              :    as seen in the gimple seen by the analyzer, and as specified
     122              :    in sync-builtins.def.  */
     123              : 
     124         3423 : class kf_atomic_exchange : public internal_known_function
     125              : {
     126              : public:
     127              :   /* This is effectively:
     128              :        tmpA = *PTR;
     129              :        tmpB = *VAL;
     130              :        *PTR = tmpB;
     131              :        *RET = tmpA;
     132              :   */
     133            4 :   void impl_call_pre (const call_details &cd) const final override
     134              :   {
     135            4 :     const svalue *num_bytes_sval = cd.get_arg_svalue (0);
     136            4 :     const svalue *ptr_sval = cd.get_arg_svalue (1);
     137            4 :     tree ptr_tree = cd.get_arg_tree (1);
     138            4 :     const svalue *val_sval = cd.get_arg_svalue (2);
     139            4 :     tree val_tree = cd.get_arg_tree (2);
     140            4 :     const svalue *ret_sval = cd.get_arg_svalue (3);
     141            4 :     tree ret_tree = cd.get_arg_tree (3);
     142              :     /* Ignore the memorder param.  */
     143              : 
     144            4 :     region_model *model = cd.get_model ();
     145            4 :     region_model_context *ctxt = cd.get_ctxt ();
     146              : 
     147            4 :     const region *ptr_reg = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
     148            4 :     const region *val_reg = model->deref_rvalue (val_sval, val_tree, ctxt);
     149            4 :     const region *ret_reg = model->deref_rvalue (ret_sval, ret_tree, ctxt);
     150              : 
     151            4 :     const svalue *tmp_a_sval
     152            4 :       = model->read_bytes (ptr_reg, ptr_tree, num_bytes_sval, ctxt);
     153            4 :     const svalue *tmp_b_sval
     154            4 :       = model->read_bytes (val_reg, val_tree, num_bytes_sval, ctxt);
     155            4 :     model->write_bytes (ptr_reg, num_bytes_sval, tmp_b_sval, ctxt);
     156            4 :     model->write_bytes (ret_reg, num_bytes_sval, tmp_a_sval, ctxt);
     157            4 :   }
     158              : };
     159              : 
     160              : /* Handler for:
     161              :    __atomic_exchange_n (type *ptr, type val, int memorder).  */
     162              : 
     163        20538 : class kf_atomic_exchange_n : public internal_known_function
     164              : {
     165              : public:
     166              :   /* This is effectively:
     167              :        RET = *PTR;
     168              :        *PTR = VAL;
     169              :        return RET;
     170              :   */
     171           39 :   void impl_call_pre (const call_details &cd) const final override
     172              :   {
     173           39 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     174           39 :     tree ptr_tree = cd.get_arg_tree (0);
     175           39 :     const svalue *set_sval = cd.get_arg_svalue (1);
     176              :     /* Ignore the memorder param.  */
     177              : 
     178           39 :     region_model *model = cd.get_model ();
     179           39 :     region_model_context *ctxt = cd.get_ctxt ();
     180              : 
     181           39 :     const region *dst_region = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
     182           39 :     const svalue *ret_sval = model->get_store_value (dst_region, ctxt);
     183           39 :     model->set_value (dst_region, set_sval, ctxt);
     184           39 :     cd.maybe_set_lhs (ret_sval);
     185           39 :   }
     186              : };
     187              : 
     188              : /* Handler for:
     189              :    type __atomic_fetch_add (type *ptr, type val, int memorder);
     190              :    type __atomic_fetch_sub (type *ptr, type val, int memorder);
     191              :    type __atomic_fetch_and (type *ptr, type val, int memorder);
     192              :    type __atomic_fetch_xor (type *ptr, type val, int memorder);
     193              :    type __atomic_fetch_or (type *ptr, type val, int memorder);
     194              : */
     195              : 
     196              : class kf_atomic_fetch_op : public internal_known_function
     197              : {
     198              : public:
     199        85575 :   kf_atomic_fetch_op (enum tree_code op): m_op (op) {}
     200              : 
     201              :   /* This is effectively:
     202              :        RET = *PTR;
     203              :        *PTR = RET OP VAL;
     204              :        return RET;
     205              :   */
     206           68 :   void impl_call_pre (const call_details &cd) const final override
     207              :   {
     208           68 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     209           68 :     tree ptr_tree = cd.get_arg_tree (0);
     210           68 :     const svalue *val_sval = cd.get_arg_svalue (1);
     211              :     /* Ignore the memorder param.  */
     212              : 
     213           68 :     region_model *model = cd.get_model ();
     214           68 :     region_model_manager *mgr = cd.get_manager ();
     215           68 :     region_model_context *ctxt = cd.get_ctxt ();
     216              : 
     217           68 :     const region *star_ptr_region
     218           68 :       = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
     219           68 :     const svalue *old_sval = model->get_store_value (star_ptr_region, ctxt);
     220          136 :     const svalue *new_sval = mgr->get_or_create_binop (old_sval->get_type (),
     221           68 :                                                        m_op,
     222              :                                                        old_sval, val_sval);
     223           68 :     model->set_value (star_ptr_region, new_sval, ctxt);
     224           68 :     cd.maybe_set_lhs (old_sval);
     225           68 :   }
     226              : 
     227              : private:
     228              :   enum tree_code m_op;
     229              : };
     230              : 
     231              : /* Handler for:
     232              :    type __atomic_add_fetch (type *ptr, type val, int memorder);
     233              :    type __atomic_sub_fetch (type *ptr, type val, int memorder);
     234              :    type __atomic_and_fetch (type *ptr, type val, int memorder);
     235              :    type __atomic_xor_fetch (type *ptr, type val, int memorder);
     236              :    type __atomic_or_fetch (type *ptr, type val, int memorder);
     237              : */
     238              : 
     239              : class kf_atomic_op_fetch : public internal_known_function
     240              : {
     241              : public:
     242        85575 :   kf_atomic_op_fetch (enum tree_code op): m_op (op) {}
     243              : 
     244              :   /* This is effectively:
     245              :        *PTR = RET OP VAL;
     246              :        return *PTR;
     247              :   */
     248           10 :   void impl_call_pre (const call_details &cd) const final override
     249              :   {
     250           10 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     251           10 :     tree ptr_tree = cd.get_arg_tree (0);
     252           10 :     const svalue *val_sval = cd.get_arg_svalue (1);
     253              :     /* Ignore the memorder param.  */
     254              : 
     255           10 :     region_model *model = cd.get_model ();
     256           10 :     region_model_manager *mgr = cd.get_manager ();
     257           10 :     region_model_context *ctxt = cd.get_ctxt ();
     258              : 
     259           10 :     const region *star_ptr_region
     260           10 :       = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
     261           10 :     const svalue *old_sval = model->get_store_value (star_ptr_region, ctxt);
     262           20 :     const svalue *new_sval = mgr->get_or_create_binop (old_sval->get_type (),
     263           10 :                                                        m_op,
     264              :                                                        old_sval, val_sval);
     265           10 :     model->set_value (star_ptr_region, new_sval, ctxt);
     266           10 :     cd.maybe_set_lhs (new_sval);
     267           10 :   }
     268              : 
     269              : private:
     270              :   enum tree_code m_op;
     271              : };
     272              : 
     273              : /* Handler for __atomic_load.
     274              :    Although the user-facing documentation specifies it as having this
     275              :    signature:
     276              : 
     277              :       void __atomic_load (type *ptr, type *ret, int memorder)
     278              : 
     279              :    by the time the C/C++ frontends have acted on it, any calls that
     280              :    can't be mapped to a _N variation end up with this signature:
     281              : 
     282              :       void __atomic_load (size_t sz, const void *src, void *dst, int memorder);
     283              : 
     284              :    as seen in the gimple seen by the analyzer, and as specified
     285              :    in sync-builtins.def.  */
     286              : 
     287         3423 : class kf_atomic_load : public internal_known_function
     288              : {
     289              : public:
     290              :   /* This is effectively:
     291              :        memmove (dst, src, sz);
     292              :   */
     293           12 :   void impl_call_pre (const call_details &cd) const final override
     294              :   {
     295           12 :     const svalue *num_bytes_sval = cd.get_arg_svalue (0);
     296           12 :     const svalue *src_sval = cd.get_arg_svalue (1);
     297           12 :     tree src_tree = cd.get_arg_tree (1);
     298           12 :     const svalue *dst_sval = cd.get_arg_svalue (2);
     299           12 :     tree dst_tree = cd.get_arg_tree (2);
     300              :     /* Ignore the memorder param.  */
     301              : 
     302           12 :     region_model *model = cd.get_model ();
     303           12 :     region_model_context *ctxt = cd.get_ctxt ();
     304              : 
     305           12 :     const region *dst_reg = model->deref_rvalue (dst_sval, dst_tree, ctxt);
     306           12 :     const region *src_reg = model->deref_rvalue (src_sval, src_tree, ctxt);
     307              : 
     308           12 :     const svalue *data_sval
     309           12 :       = model->read_bytes (src_reg, src_tree, num_bytes_sval, ctxt);
     310           12 :     model->write_bytes (dst_reg, num_bytes_sval, data_sval, ctxt);
     311           12 :   }
     312              : };
     313              : 
     314              : /* Handler for __atomic_store.
     315              :    Although the user-facing documentation specifies it as having this
     316              :    signature:
     317              : 
     318              :       void __atomic_store (type *ptr, type *val, int memorder)
     319              : 
     320              :    by the time the C/C++ frontends have acted on it, any calls that
     321              :    can't be mapped to a _N variation end up with this signature:
     322              : 
     323              :       void __atomic_store (size_t sz, type *dst, type *src, int memorder)
     324              : 
     325              :    as seen in the gimple seen by the analyzer, and as specified
     326              :    in sync-builtins.def.  */
     327              : 
     328         3423 : class kf_atomic_store : public internal_known_function
     329              : {
     330              : public:
     331              :   /* This is effectively:
     332              :        memmove (dst, src, sz);
     333              :   */
     334            4 :   void impl_call_pre (const call_details &cd) const final override
     335              :   {
     336            4 :     const svalue *num_bytes_sval = cd.get_arg_svalue (0);
     337            4 :     const svalue *dst_sval = cd.get_arg_svalue (1);
     338            4 :     tree dst_tree = cd.get_arg_tree (1);
     339            4 :     const svalue *src_sval = cd.get_arg_svalue (2);
     340            4 :     tree src_tree = cd.get_arg_tree (2);
     341              :     /* Ignore the memorder param.  */
     342              : 
     343            4 :     region_model *model = cd.get_model ();
     344            4 :     region_model_context *ctxt = cd.get_ctxt ();
     345              : 
     346            4 :     const region *dst_reg = model->deref_rvalue (dst_sval, dst_tree, ctxt);
     347            4 :     const region *src_reg = model->deref_rvalue (src_sval, src_tree, ctxt);
     348              : 
     349            4 :     const svalue *data_sval
     350            4 :       = model->read_bytes (src_reg, src_tree, num_bytes_sval, ctxt);
     351            4 :     model->write_bytes (dst_reg, num_bytes_sval, data_sval, ctxt);
     352            4 :   }
     353              : };
     354              : 
     355              : /* Handler for:
     356              :    type __atomic_load_n (type *ptr, int memorder) */
     357              : 
     358        20538 : class kf_atomic_load_n : public internal_known_function
     359              : {
     360              : public:
     361              :   /* This is effectively:
     362              :        RET = *PTR;
     363              :        return RET;
     364              :   */
     365           41 :   void impl_call_pre (const call_details &cd) const final override
     366              :   {
     367           41 :     const svalue *ptr_ptr_sval = cd.get_arg_svalue (0);
     368           41 :     tree ptr_ptr_tree = cd.get_arg_tree (0);
     369              :     /* Ignore the memorder param.  */
     370              : 
     371           41 :     region_model *model = cd.get_model ();
     372           41 :     region_model_context *ctxt = cd.get_ctxt ();
     373              : 
     374           41 :     const region *ptr_region
     375           41 :       = model->deref_rvalue (ptr_ptr_sval, ptr_ptr_tree, ctxt);
     376           41 :     const svalue *star_ptr_sval = model->get_store_value (ptr_region, ctxt);
     377           41 :     cd.maybe_set_lhs (star_ptr_sval);
     378           41 :   }
     379              : };
     380              : 
     381              : /* Handler for:
     382              :    void __atomic_store_n (type *ptr, type val, int memorder) */
     383              : 
     384        20538 : class kf_atomic_store_n : public internal_known_function
     385              : {
     386              : public:
     387              :   /* This is effectively:
     388              :        *PTR = VAL;
     389              :   */
     390           10 :   void impl_call_pre (const call_details &cd) const final override
     391              :   {
     392           10 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     393           10 :     tree ptr_tree = cd.get_arg_tree (0);
     394           10 :     const svalue *new_sval = cd.get_arg_svalue (1);
     395              :     /* Ignore the memorder param.  */
     396              : 
     397           10 :     region_model *model = cd.get_model ();
     398           10 :     region_model_context *ctxt = cd.get_ctxt ();
     399              : 
     400           10 :     const region *star_ptr_region
     401           10 :       = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
     402           10 :     model->set_value (star_ptr_region, new_sval, ctxt);
     403           10 :   }
     404              : };
     405              : 
     406              : /* Handler for "__builtin_expect" etc.  */
     407              : 
     408        10269 : class kf_expect : public internal_known_function
     409              : {
     410              : public:
     411           63 :   void impl_call_pre (const call_details &cd) const final override
     412              :   {
     413              :     /* __builtin_expect's return value is its initial argument.  */
     414           63 :     const svalue *sval = cd.get_arg_svalue (0);
     415           63 :     cd.maybe_set_lhs (sval);
     416           63 :   }
     417              : };
     418              : 
     419              : /* Handler for "calloc".  */
     420              : 
     421        10269 : class kf_calloc : public builtin_known_function
     422              : {
     423              : public:
     424         1576 :   bool matches_call_types_p (const call_details &cd) const final override
     425              :   {
     426         1576 :     return (cd.num_args () == 2
     427         1576 :             && cd.arg_is_size_p (0)
     428         3152 :             && cd.arg_is_size_p (1));
     429              :   }
     430         1002 :   enum built_in_function builtin_code () const final override
     431              :   {
     432         1002 :     return BUILT_IN_CALLOC;
     433              :   }
     434              : 
     435              :   void impl_call_pre (const call_details &cd) const final override;
     436              : };
     437              : 
     438              : void
     439          454 : kf_calloc::impl_call_pre (const call_details &cd) const
     440              : {
     441          454 :   region_model *model = cd.get_model ();
     442          454 :   region_model_manager *mgr = cd.get_manager ();
     443          454 :   const svalue *nmemb_sval = cd.get_arg_svalue (0);
     444          454 :   const svalue *size_sval = cd.get_arg_svalue (1);
     445              :   /* TODO: check for overflow here?  */
     446          454 :   const svalue *prod_sval
     447          454 :     = mgr->get_or_create_binop (size_type_node, MULT_EXPR,
     448              :                                 nmemb_sval, size_sval);
     449          454 :   const region *new_reg
     450          454 :     = model->get_or_create_region_for_heap_alloc (prod_sval, cd.get_ctxt ());
     451          454 :   const region *sized_reg
     452          454 :     = mgr->get_sized_region (new_reg, NULL_TREE, prod_sval);
     453          454 :   model->zero_fill_region (sized_reg, cd.get_ctxt ());
     454          454 :   if (cd.get_lhs_type ())
     455              :     {
     456          454 :       const svalue *ptr_sval
     457          454 :         = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     458          454 :       cd.maybe_set_lhs (ptr_sval);
     459              :     }
     460          454 : }
     461              : 
     462              : /* Handler for glibc's "__errno_location".  */
     463              : 
     464        17115 : class kf_errno_location : public known_function
     465              : {
     466              : public:
     467         3288 :   bool matches_call_types_p (const call_details &cd) const final override
     468              :   {
     469         3288 :     return cd.num_args () == 0;
     470              :   }
     471              : 
     472          519 :   void impl_call_pre (const call_details &cd) const final override
     473              :   {
     474          519 :     if (cd.get_lhs_region ())
     475              :       {
     476          519 :         region_model_manager *mgr = cd.get_manager ();
     477          519 :         const region *errno_reg = mgr->get_errno_region ();
     478          519 :         const svalue *errno_ptr = mgr->get_ptr_svalue (cd.get_lhs_type (),
     479              :                                                        errno_reg);
     480          519 :         cd.maybe_set_lhs (errno_ptr);
     481              :       }
     482          519 :   }
     483              : };
     484              : 
     485              : /* Handler for "error" and "error_at_line" from GNU's non-standard <error.h>.
     486              :    MIN_ARGS identifies the minimum number of expected arguments
     487              :    to be consistent with such a call (3 and 5 respectively).  */
     488              : 
     489              : class kf_error : public known_function
     490              : {
     491              : public:
     492        13692 :   kf_error (unsigned min_args) : m_min_args (min_args) {}
     493              : 
     494          467 :   bool matches_call_types_p (const call_details &cd) const final override
     495              :   {
     496          467 :     return (cd.num_args () >= m_min_args
     497          467 :             && cd.get_arg_type (0) == integer_type_node);
     498              :   }
     499              : 
     500              :   void impl_call_pre (const call_details &cd) const final override;
     501              : 
     502              : private:
     503              :   unsigned m_min_args;
     504              : };
     505              : 
     506              : void
     507           44 : kf_error::impl_call_pre (const call_details &cd) const
     508              : {
     509              :   /* The process exits if status != 0, so it only continues
     510              :      for the case where status == 0.
     511              :      Add that constraint, or terminate this analysis path.  */
     512           44 :   tree status = cd.get_arg_tree (0);
     513           44 :   region_model_context *ctxt = cd.get_ctxt ();
     514           44 :   region_model *model = cd.get_model ();
     515           44 :   if (!model->add_constraint (status, EQ_EXPR, integer_zero_node, ctxt))
     516           15 :     if (ctxt)
     517           15 :       ctxt->terminate_path ();
     518              : 
     519              :   /* Check "format" arg.  */
     520           44 :   const int fmt_arg_idx = (m_min_args == 3) ? 2 : 4;
     521           44 :   model->check_for_null_terminated_string_arg (cd, fmt_arg_idx);
     522           44 : }
     523              : 
     524              : /* Handler for fopen.
     525              :      FILE *fopen (const char *filename, const char *mode);
     526              :    See e.g. https://en.cppreference.com/w/c/io/fopen
     527              :    https://www.man7.org/linux/man-pages/man3/fopen.3.html
     528              :    https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170  */
     529              : 
     530         3423 : class kf_fopen : public known_function
     531              : {
     532              : public:
     533         2889 :   bool matches_call_types_p (const call_details &cd) const final override
     534              :   {
     535         2889 :     return (cd.num_args () == 2
     536         2889 :             && cd.arg_is_pointer_p (0)
     537         5778 :             && cd.arg_is_pointer_p (1));
     538              :   }
     539              : 
     540          780 :   void impl_call_pre (const call_details &cd) const final override
     541              :   {
     542          780 :     cd.check_for_null_terminated_string_arg (0);
     543          780 :     cd.check_for_null_terminated_string_arg (1);
     544          780 :     cd.set_any_lhs_with_defaults ();
     545              : 
     546              :     /* fopen's mode param is effectively a mini-DSL, but there are various
     547              :        non-standard extensions, so we don't bother to check it.  */
     548          780 :   }
     549              : };
     550              : 
     551              : /* Handler for "free", after sm-handling.
     552              : 
     553              :    If the ptr points to an underlying heap region, delete the region,
     554              :    poisoning pointers to it and regions within it.
     555              : 
     556              :    We delay this until after sm-state has been updated so that the
     557              :    sm-handling can transition all of the various casts of the pointer
     558              :    to a "freed" state *before* we delete the related region here.
     559              : 
     560              :    This has to be done here so that the sm-handling can use the fact
     561              :    that they point to the same region to establish that they are equal
     562              :    (in region_model::eval_condition), and thus transition
     563              :    all pointers to the region to the "freed" state together, regardless
     564              :    of casts.  */
     565              : 
     566        10269 : class kf_free : public builtin_known_function
     567              : {
     568              : public:
     569        68291 :   bool matches_call_types_p (const call_details &cd) const final override
     570              :   {
     571        68291 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
     572              :   }
     573        65790 :   enum built_in_function builtin_code () const final override
     574              :   {
     575        65790 :     return BUILT_IN_FREE;
     576              :   }
     577              :   void impl_call_post (const call_details &cd) const final override;
     578              : };
     579              : 
     580              : void
     581        12695 : kf_free::impl_call_post (const call_details &cd) const
     582              : {
     583        12695 :   const svalue *ptr_sval = cd.get_arg_svalue (0);
     584        12695 :   if (const region *freed_reg = ptr_sval->maybe_get_region ())
     585              :     {
     586              :       /* If the ptr points to an underlying heap region, delete it,
     587              :          poisoning pointers.  */
     588        10232 :       region_model *model = cd.get_model ();
     589        10232 :       model->unbind_region_and_descendents (freed_reg, poison_kind::freed);
     590        10232 :       model->unset_dynamic_extents (freed_reg);
     591              :     }
     592        12695 : }
     593              : 
     594              : /* Handle the on_call_pre part of "malloc".  */
     595              : 
     596        10269 : class kf_malloc : public builtin_known_function
     597              : {
     598              : public:
     599        45057 :   bool matches_call_types_p (const call_details &cd) const final override
     600              :   {
     601        45057 :     return (cd.num_args () == 1
     602        45057 :             && cd.arg_is_size_p (0));
     603              :   }
     604        36228 :   enum built_in_function builtin_code () const final override
     605              :   {
     606        36228 :     return BUILT_IN_MALLOC;
     607              :   }
     608              :   void impl_call_pre (const call_details &cd) const final override;
     609              : };
     610              : 
     611              : void
     612        10444 : kf_malloc::impl_call_pre (const call_details &cd) const
     613              : {
     614        10444 :   region_model *model = cd.get_model ();
     615        10444 :   region_model_manager *mgr = cd.get_manager ();
     616        10444 :   const svalue *size_sval = cd.get_arg_svalue (0);
     617        10444 :   const region *new_reg
     618        10444 :     = model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
     619        10444 :   if (cd.get_lhs_type ())
     620              :     {
     621        10444 :       const svalue *ptr_sval
     622        10444 :         = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     623        10444 :       cd.maybe_set_lhs (ptr_sval);
     624              :     }
     625        10444 : }
     626              : 
     627              : /* Handler for "memcpy" and "__builtin_memcpy",
     628              :    "memmove", and "__builtin_memmove".  */
     629              : 
     630              : class kf_memcpy_memmove : public builtin_known_function
     631              : {
     632              : public:
     633              :   enum kf_memcpy_memmove_variant
     634              :   {
     635              :     KF_MEMCPY,
     636              :     KF_MEMCPY_CHK,
     637              :     KF_MEMMOVE,
     638              :     KF_MEMMOVE_CHK,
     639              :   };
     640        34230 :   kf_memcpy_memmove (enum kf_memcpy_memmove_variant variant)
     641        34230 :     : m_variant (variant) {};
     642         3270 :   bool matches_call_types_p (const call_details &cd) const final override
     643              :   {
     644         3270 :     return (cd.num_args () == 3
     645         3270 :             && cd.arg_is_pointer_p (0)
     646         3270 :             && cd.arg_is_pointer_p (1)
     647         6540 :             && cd.arg_is_size_p (2));
     648              :   }
     649         3448 :   enum built_in_function builtin_code () const final override
     650              :   {
     651         3448 :     switch (m_variant)
     652              :       {
     653              :       case KF_MEMCPY:
     654              :         return BUILT_IN_MEMCPY;
     655              :       case KF_MEMCPY_CHK:
     656              :         return BUILT_IN_MEMCPY_CHK;
     657              :       case KF_MEMMOVE:
     658              :         return BUILT_IN_MEMMOVE;
     659              :       case KF_MEMMOVE_CHK:
     660              :         return BUILT_IN_MEMMOVE_CHK;
     661            0 :       default:
     662            0 :         gcc_unreachable ();
     663              :       }
     664              :   }
     665              :   void impl_call_pre (const call_details &cd) const final override;
     666              : private:
     667              :   const enum kf_memcpy_memmove_variant m_variant;
     668              : };
     669              : 
     670              : void
     671          564 : kf_memcpy_memmove::impl_call_pre (const call_details &cd) const
     672              : {
     673          564 :   const svalue *dest_ptr_sval = cd.get_arg_svalue (0);
     674          564 :   const svalue *src_ptr_sval = cd.get_arg_svalue (1);
     675          564 :   const svalue *num_bytes_sval = cd.get_arg_svalue (2);
     676              : 
     677          564 :   region_model *model = cd.get_model ();
     678              : 
     679          564 :   const region *dest_reg
     680          564 :     = model->deref_rvalue (dest_ptr_sval, cd.get_arg_tree (0), cd.get_ctxt ());
     681          564 :   const region *src_reg
     682          564 :     = model->deref_rvalue (src_ptr_sval, cd.get_arg_tree (1), cd.get_ctxt ());
     683              : 
     684          564 :   cd.maybe_set_lhs (dest_ptr_sval);
     685              :   /* Check for overlap.  */
     686          564 :   switch (m_variant)
     687              :     {
     688          543 :     case KF_MEMCPY:
     689          543 :     case KF_MEMCPY_CHK:
     690          543 :       cd.complain_about_overlap (0, 1, num_bytes_sval);
     691          543 :       break;
     692              : 
     693              :     case KF_MEMMOVE:
     694              :     case KF_MEMMOVE_CHK:
     695              :       /* It's OK for memmove's arguments to overlap.  */
     696              :       break;
     697              : 
     698            0 :     default:
     699            0 :         gcc_unreachable ();
     700              :     }
     701          564 :   model->copy_bytes (dest_reg,
     702              :                      src_reg, cd.get_arg_tree (1),
     703              :                      num_bytes_sval,
     704              :                      cd.get_ctxt ());
     705          564 : }
     706              : 
     707              : /* Handler for "memset" and "__builtin_memset".  */
     708              : 
     709              : class kf_memset : public builtin_known_function
     710              : {
     711              : public:
     712        17115 :   kf_memset (bool chk_variant) : m_chk_variant (chk_variant) {}
     713         3675 :   bool matches_call_types_p (const call_details &cd) const final override
     714              :   {
     715         3675 :     return (cd.num_args () == 3 && cd.arg_is_pointer_p (0));
     716              :   }
     717         3416 :   enum built_in_function builtin_code () const final override
     718              :   {
     719         3416 :     return m_chk_variant ? BUILT_IN_MEMSET_CHK : BUILT_IN_MEMSET;
     720              :   }
     721              :   void impl_call_pre (const call_details &cd) const final override;
     722              : private:
     723              :   const bool m_chk_variant;
     724              : };
     725              : 
     726              : void
     727          742 : kf_memset::impl_call_pre (const call_details &cd) const
     728              : {
     729          742 :   const svalue *dest_sval = cd.get_arg_svalue (0);
     730          742 :   const svalue *fill_value_sval = cd.get_arg_svalue (1);
     731          742 :   const svalue *num_bytes_sval = cd.get_arg_svalue (2);
     732              : 
     733          742 :   region_model *model = cd.get_model ();
     734          742 :   region_model_manager *mgr = cd.get_manager ();
     735              : 
     736          742 :   const region *dest_reg
     737          742 :     = model->deref_rvalue (dest_sval, cd.get_arg_tree (0), cd.get_ctxt ());
     738              : 
     739          742 :   const svalue *fill_value_u8
     740          742 :     = mgr->get_or_create_cast (unsigned_char_type_node, fill_value_sval);
     741              : 
     742          742 :   const region *sized_dest_reg = mgr->get_sized_region (dest_reg,
     743              :                                                         NULL_TREE,
     744              :                                                         num_bytes_sval);
     745          742 :   model->fill_region (sized_dest_reg, fill_value_u8, cd.get_ctxt ());
     746              : 
     747          742 :   cd.maybe_set_lhs (dest_sval);
     748          742 : }
     749              : 
     750              : /* A subclass of pending_diagnostic for complaining about functions on the
     751              :    'mktemp' family called on a string literal.  */
     752              : 
     753              : class mktemp_of_string_literal : public undefined_function_behavior
     754              : {
     755              : public:
     756           20 :   mktemp_of_string_literal (const call_details &cd)
     757           20 :     : undefined_function_behavior (cd)
     758              :   {
     759              :   }
     760              : 
     761              :   int
     762           40 :   get_controlling_option () const final override
     763              :   {
     764           40 :     return OPT_Wanalyzer_mktemp_of_string_literal;
     765              :   }
     766              : 
     767              :   bool
     768           20 :   emit (diagnostic_emission_context &ctxt) final override
     769              :   {
     770           20 :     auto_diagnostic_group d;
     771              : 
     772              :     /* SEI CERT C Coding Standard: "STR30-C. Do not attempt to modify string
     773              :        literals.  */
     774           20 :     diagnostics::metadata::precanned_rule rule (
     775           20 :       "STR30-C", "https://wiki.sei.cmu.edu/confluence/x/VtYxBQ");
     776           20 :     ctxt.add_rule (rule);
     777              : 
     778           20 :     bool warned = ctxt.warn ("%qE on a string literal", get_callee_fndecl ());
     779           20 :     if (warned)
     780           20 :       inform (ctxt.get_location (),
     781              :               "use a writable character array as the template argument,"
     782              :               " e.g. %<char tmpl[] = \"/tmp/fooXXXXXX\"%>");
     783           40 :     return warned;
     784           20 :   }
     785              : 
     786              :   bool
     787           40 :   describe_final_event (pretty_printer &pp,
     788              :                         const evdesc::final_event &) final override
     789              :   {
     790           40 :     pp_printf (&pp, "%qE on a string literal", get_callee_fndecl ());
     791           40 :     return true;
     792              :   }
     793              : };
     794              : 
     795              : /* A subclass of pending_diagnostic for complaining about functions in the
     796              :    'mktemp' family called with a template that does not contain the expected
     797              :    "XXXXXX" placeholder.  */
     798              : 
     799              : class mktemp_missing_placeholder
     800              :   : public pending_diagnostic_subclass<mktemp_missing_placeholder>
     801              : {
     802              : public:
     803           34 :   mktemp_missing_placeholder (const call_details &cd, size_t trailing_len)
     804           68 :     : m_call_stmt (cd.get_call_stmt ()), m_fndecl (cd.get_fndecl_for_call ()),
     805           34 :       m_trailing_len (trailing_len)
     806              :   {
     807           34 :     gcc_assert (m_fndecl);
     808           34 :   }
     809              : 
     810              :   const char *
     811         1007 :   get_kind () const final override
     812              :   {
     813         1007 :     return "mktemp_missing_placeholder";
     814              :   }
     815              : 
     816              :   bool
     817           34 :   operator== (const mktemp_missing_placeholder &other) const
     818              :   {
     819           34 :     return &m_call_stmt == &other.m_call_stmt;
     820              :   }
     821              : 
     822              :   int
     823           68 :   get_controlling_option () const final override
     824              :   {
     825           68 :     return OPT_Wanalyzer_mktemp_missing_placeholder;
     826              :   }
     827              : 
     828              :   bool
     829           34 :   emit (diagnostic_emission_context &ctxt) final override
     830              :   {
     831           34 :     if (m_trailing_len == 0)
     832           24 :       return ctxt.warn ("%qE template string does not end with %qs", m_fndecl,
     833           24 :                         "XXXXXX");
     834              :     else
     835           10 :       return ctxt.warn ("%qE template string does not contain %qs"
     836              :                         " before a %zu-character suffix",
     837           10 :                         m_fndecl, "XXXXXX", m_trailing_len);
     838              :   }
     839              : 
     840              :   bool
     841           68 :   describe_final_event (pretty_printer &pp,
     842              :                         const evdesc::final_event &) final override
     843              :   {
     844           68 :     if (m_trailing_len == 0)
     845           48 :       pp_printf (&pp, "%qE template string does not end with %qs", m_fndecl,
     846              :                  "XXXXXX");
     847              :     else
     848           20 :       pp_printf (&pp,
     849              :                  "%qE template string does not contain %qs"
     850              :                  " before a %zu-character suffix",
     851              :                  m_fndecl, "XXXXXX", m_trailing_len);
     852           68 :     return true;
     853              :   }
     854              : 
     855              : private:
     856              :   const gimple &m_call_stmt;
     857              :   tree m_fndecl; // non-NULL
     858              :   size_t m_trailing_len;
     859              : };
     860              : 
     861              : /* A subclass of pending_diagnostic for complaining about 'mkostemp'
     862              :    or 'mkostemps' called with flags that are already included
     863              :    internally (O_CREAT, O_EXCL, O_RDWR).  */
     864              : 
     865              : class mkostemp_redundant_flags
     866              :   : public pending_diagnostic_subclass<mkostemp_redundant_flags>
     867              : {
     868              : public:
     869            8 :   mkostemp_redundant_flags (const call_details &cd)
     870            8 :     : m_call_stmt (cd.get_call_stmt ()), m_fndecl (cd.get_fndecl_for_call ())
     871              :   {
     872            8 :     gcc_assert (m_fndecl);
     873            8 :   }
     874              : 
     875              :   const char *
     876          484 :   get_kind () const final override
     877              :   {
     878          484 :     return "mkostemp_redundant_flags";
     879              :   }
     880              : 
     881              :   bool
     882            8 :   operator== (const mkostemp_redundant_flags &other) const
     883              :   {
     884            8 :     return &m_call_stmt == &other.m_call_stmt;
     885              :   }
     886              : 
     887              :   int
     888           16 :   get_controlling_option () const final override
     889              :   {
     890           16 :     return OPT_Wanalyzer_mkostemp_redundant_flags;
     891              :   }
     892              : 
     893              :   bool
     894            8 :   emit (diagnostic_emission_context &ctxt) final override
     895              :   {
     896            8 :     return ctxt.warn (
     897              :       "%qE flags argument should not include %<O_RDWR%>, %<O_CREAT%>,"
     898              :       " or %<O_EXCL%> as these are already implied",
     899            8 :       m_fndecl);
     900              :   }
     901              : 
     902              :   bool
     903           16 :   describe_final_event (pretty_printer &pp,
     904              :                         const evdesc::final_event &) final override
     905              :   {
     906           16 :     pp_printf (&pp,
     907              :                "%qE flags argument should not include %<O_RDWR%>, %<O_CREAT%>,"
     908              :                " or %<O_EXCL%> as these are already implied",
     909              :                m_fndecl);
     910           16 :     return true;
     911              :   }
     912              : 
     913              : private:
     914              :   const gimple &m_call_stmt;
     915              :   tree m_fndecl; // non-NULL
     916              : };
     917              : 
     918              : class kf_mktemp_family : public known_function
     919              : {
     920              : public:
     921              :   /* Describes how the mktemp-family function signals success or failure
     922              :      through its return value.  */
     923              :   enum class outcome
     924              :   {
     925              :     /* Returns fd on success, -1 on failure (mkstemp, mkostemp, etc.).  */
     926              :     fd,
     927              : 
     928              :     /* Returns pointer on success, NULL on failure (mkdtemp).  */
     929              :     null_ptr,
     930              : 
     931              :     /* Returns template pointer; first byte \0 on failure (mktemp).  */
     932              :     modif_tmpl
     933              :   };
     934              : 
     935              : protected:
     936              :   /* suffixlen_arg_idx is the index of the suffixlen argument, or -1
     937              :      if there is none (trailing_len is implicitly 0).  */
     938        20538 :   kf_mktemp_family (outcome oc, int suffixlen_arg_idx)
     939        20538 :     : m_outcome (oc), m_suffixlen_arg_idx (suffixlen_arg_idx)
     940              :   {
     941              :   }
     942              : 
     943              :   class failure;
     944              :   class success;
     945              : 
     946              :   static void check_for_string_literal_arg (const call_details &cd);
     947              : 
     948              :   /* Check whether the flags argument at FLAGS_ARG_IDX contains any of
     949              :      O_RDWR, O_CREAT, or O_EXCL, which are already included internally
     950              :      by mkostemp/mkostemps.  */
     951              :   static void check_flags (const call_details &cd, unsigned int flags_arg_idx);
     952              : 
     953              :   HOST_WIDE_INT get_trailing_len (const call_details &cd,
     954              :                                   tristate &valid) const;
     955              : 
     956              :   void impl_call_post (const call_details &cd) const final override;
     957              : 
     958              : private:
     959              :   outcome m_outcome;
     960              :   int m_suffixlen_arg_idx;
     961              : 
     962              :   static const int PLACEHOLDER_LEN = 6;
     963              : 
     964              :   /* Return true if the placeholder is "XXXXXX", false if it definitely isn't,
     965              :    or unknown if we can't determine.  */
     966              :   static tristate check_placeholder (const call_details &cd,
     967              :                                      size_t trailing_len,
     968              :                                      const svalue *ptr_sval,
     969              :                                      const svalue *strlen_sval);
     970              : };
     971              : 
     972              : void
     973          122 : kf_mktemp_family::check_for_string_literal_arg (const call_details &cd)
     974              : {
     975          122 :   region_model_context *ctxt = cd.get_ctxt ();
     976          122 :   gcc_assert (ctxt);
     977          122 :   cd.get_model ()->check_for_null_terminated_string_arg (cd, 0, false,
     978              :                                                          nullptr);
     979          122 :   if (cd.get_arg_string_literal (0))
     980              :     {
     981           20 :       ctxt->warn (std::make_unique<mktemp_of_string_literal> (cd));
     982           20 :       ctxt->terminate_path ();
     983              :     }
     984          122 : }
     985              : 
     986              : void
     987           49 : kf_mktemp_family::check_flags (const call_details &cd,
     988              :                                unsigned int flags_arg_idx)
     989              : {
     990           49 :   region_model_context *ctxt = cd.get_ctxt ();
     991           49 :   gcc_assert (ctxt);
     992              : 
     993           49 :   const svalue *flags_sval = cd.get_arg_svalue (flags_arg_idx);
     994           49 :   const constant_svalue *cst = flags_sval->dyn_cast_constant_svalue ();
     995           49 :   if (!cst)
     996              :     return;
     997              : 
     998           39 :   unsigned HOST_WIDE_INT flags = TREE_INT_CST_LOW (cst->get_constant ());
     999              : 
    1000              :   /* Check whether any of the implicit flags are redundantly specified. */
    1001           39 :   unsigned HOST_WIDE_INT implicit_flags = 0;
    1002          156 :   for (const char *name : { "O_RDWR", "O_CREAT", "O_EXCL" })
    1003          117 :     if (tree cst_tree = get_stashed_constant_by_name (name))
    1004          117 :       implicit_flags |= TREE_INT_CST_LOW (cst_tree);
    1005              : 
    1006           39 :   if (flags & implicit_flags)
    1007            8 :     ctxt->warn (std::make_unique<mkostemp_redundant_flags> (cd));
    1008              : }
    1009              : 
    1010              : /* Extract the trailing length from the suffixlen argument.
    1011              : 
    1012              :    Returns the trailing length on success, or -1 if the suffixlen is
    1013              :    not a compile-time constant.  Sets VALID to TS_FALSE if the
    1014              :    suffixlen is a negative constant (always invalid).  */
    1015              : 
    1016              : HOST_WIDE_INT
    1017          102 : kf_mktemp_family::get_trailing_len (const call_details &cd,
    1018              :                                     tristate &valid) const
    1019              : {
    1020          102 :   if (m_suffixlen_arg_idx < 0)
    1021              :     return 0;
    1022              : 
    1023           36 :   const svalue *suffixlen_sval = cd.get_arg_svalue (m_suffixlen_arg_idx);
    1024           36 :   const constant_svalue *cst = suffixlen_sval->dyn_cast_constant_svalue ();
    1025           36 :   if (!cst)
    1026              :     return -1;
    1027              : 
    1028              :   /* TODO: Negative suffixlen is always wrong and potentially OOB, maybe add a
    1029              :      warning in the future?  */
    1030           26 :   if (tree_int_cst_sgn (cst->get_constant ()) < 0)
    1031              :     {
    1032            0 :       valid = tristate::TS_FALSE;
    1033            0 :       return -1;
    1034              :     }
    1035              : 
    1036           26 :   return TREE_INT_CST_LOW (cst->get_constant ());
    1037              : }
    1038              : 
    1039              : /* Model the failure outcome of a mktemp-family function call.
    1040              : 
    1041              :    Sets the return value according to the function's convention (fd == -1, NULL
    1042              :    pointer, or '\0' in template[0]) and sets errno.  */
    1043              : 
    1044              : class kf_mktemp_family::failure : public failed_call_info
    1045              : {
    1046              : public:
    1047          102 :   failure (const call_details &cd, outcome oc)
    1048          102 :     : failed_call_info (cd), m_outcome (oc)
    1049              :   {
    1050              :   }
    1051              : 
    1052              :   bool
    1053          102 :   update_model (region_model *model, const exploded_edge *,
    1054              :                 region_model_context *ctxt) const final override
    1055              :   {
    1056          102 :     const call_details cd (get_call_details (model, ctxt));
    1057              : 
    1058          102 :     switch (m_outcome)
    1059              :       {
    1060           73 :       case outcome::fd:
    1061           73 :         model->update_for_int_cst_return (cd, -1, true);
    1062           73 :         break;
    1063           14 :       case outcome::null_ptr:
    1064           14 :         model->update_for_null_return (cd, true);
    1065           14 :         break;
    1066           15 :       case outcome::modif_tmpl:
    1067           15 :         {
    1068           15 :           const svalue *first_arg_svalue = cd.get_arg_svalue (0);
    1069              : 
    1070              :           /* Return the same pointer that was passed in.  */
    1071           15 :           cd.maybe_set_lhs (first_arg_svalue);
    1072              : 
    1073           15 :           region_model_manager *mgr = cd.get_manager ();
    1074           15 :           const region *template_reg = model->deref_rvalue (
    1075              :             first_arg_svalue, cd.get_arg_tree (0), ctxt);
    1076              : 
    1077              :           /* mktemp may have modified the X positions before failing;
    1078              :              invalidate the old buffer contents.  */
    1079           15 :           model->mark_region_as_unknown (template_reg, nullptr);
    1080              : 
    1081              :           /* Then: template[0] = '\0'.  */
    1082           15 :           const svalue *nul = mgr->get_or_create_int_cst (char_type_node, 0);
    1083           15 :           model->set_value (template_reg, nul, ctxt);
    1084           15 :           break;
    1085              :         }
    1086            0 :       default:
    1087            0 :         gcc_unreachable ();
    1088              :       }
    1089              : 
    1090          102 :     model->set_errno (cd);
    1091          102 :     return true;
    1092              :   }
    1093              : 
    1094              : private:
    1095              :   outcome m_outcome;
    1096              : };
    1097              : 
    1098              : /* Model the success outcome of a mktemp-family function call.
    1099              : 
    1100              :    For fd-returning functions, conjures a non-negative fd and marks it valid.
    1101              :    For pointer-returning functions, returns the template pointer and marks the
    1102              :    template region as modified.  */
    1103              : 
    1104              : class kf_mktemp_family::success : public success_call_info
    1105              : {
    1106              : public:
    1107           68 :   success (const call_details &cd, outcome oc)
    1108           68 :     : success_call_info (cd), m_outcome (oc)
    1109              :   {
    1110              :   }
    1111              : 
    1112              :   bool
    1113          170 :   update_model (region_model *model, const exploded_edge *,
    1114              :                 region_model_context *ctxt) const final override
    1115              :   {
    1116          170 :     const call_details cd (get_call_details (model, ctxt));
    1117              : 
    1118          170 :     switch (m_outcome)
    1119              :       {
    1120          152 :       case outcome::fd:
    1121          152 :         {
    1122          152 :           region_model_manager *mgr = cd.get_manager ();
    1123          152 :           const region *lhs_reg = cd.get_lhs_region ();
    1124              :           /* conjured_svalue needs a non-null id_reg.  When there is
    1125              :              no LHS, use an unknown symbolic region; we still conjure
    1126              :              and mark the fd so the leak checker can see it.  */
    1127          152 :           const region *id_reg
    1128          152 :             = lhs_reg ? lhs_reg
    1129          116 :                       : mgr->get_unknown_symbolic_region (integer_type_node);
    1130              : 
    1131          152 :           tree lhs_type = cd.get_lhs_type ();
    1132          152 :           if (!lhs_type)
    1133          116 :             lhs_type = integer_type_node;
    1134              : 
    1135          152 :           conjured_purge p (model, ctxt);
    1136          152 :           const svalue *fd_sval = mgr->get_or_create_conjured_svalue (
    1137          152 :             lhs_type, &cd.get_call_stmt (), id_reg, p);
    1138          152 :           const svalue *zero = mgr->get_or_create_int_cst (lhs_type, 0);
    1139              : 
    1140          152 :           if (!model->add_constraint (fd_sval, GE_EXPR, zero, ctxt))
    1141            0 :             return false;
    1142              : 
    1143          152 :           if (lhs_reg)
    1144           36 :             model->set_value (lhs_reg, fd_sval, ctxt);
    1145              : 
    1146              :           /* TODO: transition to an unchecked state so that
    1147              :              use-without-check can be detected.  */
    1148          152 :           model->mark_as_valid_fd (fd_sval, ctxt);
    1149          152 :           break;
    1150              :         }
    1151           11 :       case outcome::null_ptr:
    1152           11 :         {
    1153           11 :           region_model_manager *mgr = cd.get_manager ();
    1154           11 :           const svalue *first_arg_svalue = cd.get_arg_svalue (0);
    1155           11 :           cd.maybe_set_lhs (first_arg_svalue);
    1156              : 
    1157              :           /* On success, mkdtemp returns non-NULL.  */
    1158           11 :           const svalue *null_ptr
    1159           11 :             = mgr->get_or_create_int_cst (first_arg_svalue->get_type (), 0);
    1160           11 :           if (!model->add_constraint (first_arg_svalue, NE_EXPR, null_ptr,
    1161              :                                       ctxt))
    1162              :             return false;
    1163              : 
    1164           10 :           const region *template_reg = model->deref_rvalue (
    1165              :             first_arg_svalue, cd.get_arg_tree (0), ctxt);
    1166           10 :           model->mark_region_as_unknown (template_reg, nullptr);
    1167           10 :           break;
    1168              :         }
    1169            7 :       case outcome::modif_tmpl:
    1170            7 :         {
    1171            7 :           const svalue *first_arg_svalue = cd.get_arg_svalue (0);
    1172            7 :           cd.maybe_set_lhs (first_arg_svalue);
    1173            7 :           const region *template_reg = model->deref_rvalue (
    1174              :             first_arg_svalue, cd.get_arg_tree (0), ctxt);
    1175            7 :           model->mark_region_as_unknown (template_reg, nullptr);
    1176            7 :           break;
    1177              :         }
    1178            0 :       default:
    1179            0 :         gcc_unreachable ();
    1180              :       }
    1181              : 
    1182              :     return true;
    1183              :   }
    1184              : 
    1185              : private:
    1186              :   outcome m_outcome;
    1187              : };
    1188              : 
    1189              : /* Bifurcate into success and failure paths.  The template placeholder is
    1190              :    validated when possible.  If definitely invalid, only the failure path is
    1191              :    explored.  */
    1192              : 
    1193              : void
    1194          102 : kf_mktemp_family::impl_call_post (const call_details &cd) const
    1195              : {
    1196          102 :   if (!cd.get_ctxt ())
    1197            0 :     return;
    1198              : 
    1199          102 :   tristate valid = tristate::TS_UNKNOWN;
    1200          102 :   HOST_WIDE_INT trailing_len = get_trailing_len (cd, valid);
    1201              : 
    1202              :   /* Determine whether the template placeholder is valid.  */
    1203          102 :   const svalue *strlen_sval = nullptr;
    1204          102 :   const svalue *ptr_sval = nullptr;
    1205          102 :   if (trailing_len >= 0 && !valid.is_false ())
    1206              :     {
    1207           92 :       ptr_sval = cd.get_arg_svalue (0);
    1208           92 :       strlen_sval = cd.get_model ()->check_for_null_terminated_string_arg (
    1209              :         cd, 0, false, nullptr);
    1210              :     }
    1211              : 
    1212           92 :   if (strlen_sval)
    1213              :     {
    1214           92 :       valid = check_placeholder (cd, trailing_len, ptr_sval, strlen_sval);
    1215           92 :       if (valid.is_false ())
    1216           68 :         cd.get_ctxt ()->warn (
    1217           34 :           std::make_unique<mktemp_missing_placeholder> (cd, trailing_len));
    1218              :     }
    1219              : 
    1220              :   /* Failure is always possible (bad template or runtime failure).  */
    1221          102 :   cd.get_ctxt ()->bifurcate (std::make_unique<failure> (cd, m_outcome));
    1222              :   /* Success is only possible if the template is not definitely invalid.  */
    1223          102 :   if (!valid.is_false ())
    1224           68 :     cd.get_ctxt ()->bifurcate (std::make_unique<success> (cd, m_outcome));
    1225          102 :   cd.get_ctxt ()->terminate_path ();
    1226              : }
    1227              : 
    1228              : tristate
    1229           92 : kf_mktemp_family::check_placeholder (const call_details &cd,
    1230              :                                      size_t trailing_len,
    1231              :                                      const svalue *ptr_sval,
    1232              :                                      const svalue *strlen_sval)
    1233              : {
    1234           92 :   region_model *model = cd.get_model ();
    1235              : 
    1236           92 :   const constant_svalue *len_cst = strlen_sval->dyn_cast_constant_svalue ();
    1237           92 :   if (!len_cst)
    1238           26 :     return tristate::TS_UNKNOWN;
    1239              : 
    1240           66 :   byte_offset_t len = TREE_INT_CST_LOW (len_cst->get_constant ());
    1241           66 :   if (len < PLACEHOLDER_LEN + trailing_len)
    1242           16 :     return tristate::TS_FALSE;
    1243              : 
    1244           50 :   tree arg_tree = cd.get_arg_tree (0);
    1245           50 :   const region *reg = model->deref_rvalue (ptr_sval, arg_tree, cd.get_ctxt ());
    1246              : 
    1247              :   /* Find the byte offset of the pointed-to region. */
    1248           50 :   region_offset reg_offset = reg->get_offset (cd.get_manager ());
    1249           50 :   if (reg_offset.symbolic_p ())
    1250            0 :     return tristate::TS_UNKNOWN;
    1251           50 :   byte_offset_t ptr_byte_offset;
    1252           50 :   if (!reg_offset.get_concrete_byte_offset (&ptr_byte_offset))
    1253            0 :     return tristate::TS_UNKNOWN;
    1254              : 
    1255           50 :   const region *base_reg = reg->get_base_region ();
    1256           50 :   const svalue *base_sval = model->get_store_value (base_reg, cd.get_ctxt ());
    1257              : 
    1258           50 :   const constant_svalue *cst_sval = base_sval->dyn_cast_constant_svalue ();
    1259           50 :   if (!cst_sval)
    1260            0 :     return tristate::TS_UNKNOWN;
    1261              : 
    1262           50 :   tree cst = cst_sval->get_constant ();
    1263           50 :   if (TREE_CODE (cst) != STRING_CST)
    1264            0 :     return tristate::TS_UNKNOWN;
    1265              : 
    1266           50 :   HOST_WIDE_INT str_len = len.to_shwi ();
    1267           50 :   HOST_WIDE_INT start = ptr_byte_offset.to_shwi ();
    1268              : 
    1269              :   /* Ensure we can read up to and including the NUL terminator at position
    1270              :      [start + str_len - trailing_len] within the STRING_CST.  */
    1271           50 :   HOST_WIDE_INT range = start + str_len - trailing_len + 1;
    1272           50 :   if (range > TREE_STRING_LENGTH (cst))
    1273            0 :     return tristate::TS_UNKNOWN;
    1274              : 
    1275           50 :   if (memcmp (TREE_STRING_POINTER (cst) + start + str_len - trailing_len
    1276           50 :                 - PLACEHOLDER_LEN,
    1277              :               "XXXXXX", PLACEHOLDER_LEN)
    1278              :       != 0)
    1279           18 :     return tristate::TS_FALSE;
    1280              : 
    1281           32 :   return tristate::TS_TRUE;
    1282              : }
    1283              : 
    1284              : /* Handler for calls to "mkdtemp", "mkstemp", and "mktemp", which all
    1285              :    take a single char * template argument.
    1286              : 
    1287              :    The template must not be a string constant, and its last six
    1288              :    characters must be "XXXXXX".  */
    1289              : 
    1290              : class kf_mktemp_simple : public kf_mktemp_family
    1291              : {
    1292              : public:
    1293        10269 :   kf_mktemp_simple (outcome oc) : kf_mktemp_family (oc, -1) {}
    1294              : 
    1295              :   bool
    1296          369 :   matches_call_types_p (const call_details &cd) const final override
    1297              :   {
    1298          369 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
    1299              :   }
    1300              : 
    1301              :   void
    1302           54 :   impl_call_pre (const call_details &cd) const final override
    1303              :   {
    1304           54 :     if (cd.get_ctxt ())
    1305           54 :       check_for_string_literal_arg (cd);
    1306           54 :   }
    1307              : };
    1308              : 
    1309              : /* Handler for calls to "mkostemp":
    1310              : 
    1311              :      int mkostemp(char *template, int flags);
    1312              : 
    1313              :    The template must not be a string constant, and its last six
    1314              :    characters must be "XXXXXX".  Warns when flags contains O_RDWR,
    1315              :    O_CREAT, or O_EXCL, which are already included internally.  */
    1316              : 
    1317              : class kf_mkostemp : public kf_mktemp_family
    1318              : {
    1319              : public:
    1320         3423 :   kf_mkostemp () : kf_mktemp_family (outcome::fd, -1) {}
    1321              : 
    1322              :   bool
    1323          165 :   matches_call_types_p (const call_details &cd) const final override
    1324              :   {
    1325          330 :     return (cd.num_args () == 2 && cd.arg_is_pointer_p (0)
    1326          330 :             && cd.arg_is_integral_p (1));
    1327              :   }
    1328              : 
    1329              :   void
    1330           24 :   impl_call_pre (const call_details &cd) const final override
    1331              :   {
    1332           24 :     if (cd.get_ctxt ())
    1333              :       {
    1334           24 :         check_for_string_literal_arg (cd);
    1335           24 :         check_flags (cd, 1);
    1336              :       }
    1337           24 :   }
    1338              : };
    1339              : 
    1340              : /* Handler for calls to "mkostemps":
    1341              : 
    1342              :      int mkostemps(char *template, int suffixlen, int flags);
    1343              : 
    1344              :    The template must not be a string constant, and must contain
    1345              :    "XXXXXX" before a suffixlen-character suffix.  Warns when flags
    1346              :    contains O_RDWR, O_CREAT, or O_EXCL, which are already included
    1347              :    internally.  */
    1348              : 
    1349              : class kf_mkostemps : public kf_mktemp_family
    1350              : {
    1351              : public:
    1352         3423 :   kf_mkostemps () : kf_mktemp_family (outcome::fd, 1) {}
    1353              : 
    1354              :   bool
    1355          171 :   matches_call_types_p (const call_details &cd) const final override
    1356              :   {
    1357          342 :     return (cd.num_args () == 3 && cd.arg_is_pointer_p (0)
    1358          342 :             && cd.arg_is_integral_p (1) && cd.arg_is_integral_p (2));
    1359              :   }
    1360              : 
    1361              :   void
    1362           25 :   impl_call_pre (const call_details &cd) const final override
    1363              :   {
    1364           25 :     if (cd.get_ctxt ())
    1365              :       {
    1366           25 :         check_for_string_literal_arg (cd);
    1367           25 :         check_flags (cd, 2);
    1368              :       }
    1369           25 :   }
    1370              : };
    1371              : 
    1372              : /* Handler for calls to "mkstemps":
    1373              : 
    1374              :      int mkstemps(char *template, int suffixlen);
    1375              : 
    1376              :    The template must not be a string constant, and must contain
    1377              :    "XXXXXX" before a suffixlen-character suffix.  */
    1378              : 
    1379              : class kf_mkstemps : public kf_mktemp_family
    1380              : {
    1381              : public:
    1382         3423 :   kf_mkstemps () : kf_mktemp_family (outcome::fd, 1) {}
    1383              : 
    1384              :   bool
    1385          129 :   matches_call_types_p (const call_details &cd) const final override
    1386              :   {
    1387          258 :     return (cd.num_args () == 2 && cd.arg_is_pointer_p (0)
    1388          258 :             && cd.arg_is_integral_p (1));
    1389              :   }
    1390              : 
    1391              :   void
    1392           19 :   impl_call_pre (const call_details &cd) const final override
    1393              :   {
    1394           19 :     if (cd.get_ctxt ())
    1395           19 :       check_for_string_literal_arg (cd);
    1396           19 :   }
    1397              : };
    1398              : 
    1399              : /* A subclass of pending_diagnostic for complaining about 'putenv'
    1400              :    called on an auto var.  */
    1401              : 
    1402              : class putenv_of_auto_var
    1403              : : public pending_diagnostic_subclass<putenv_of_auto_var>
    1404              : {
    1405              : public:
    1406            7 :   putenv_of_auto_var (tree fndecl, const region *reg)
    1407            7 :   : m_fndecl (fndecl), m_reg (reg),
    1408            7 :     m_var_decl (reg->get_base_region ()->maybe_get_decl ())
    1409              :   {
    1410            7 :   }
    1411              : 
    1412          106 :   const char *get_kind () const final override
    1413              :   {
    1414          106 :     return "putenv_of_auto_var";
    1415              :   }
    1416              : 
    1417            7 :   bool operator== (const putenv_of_auto_var &other) const
    1418              :   {
    1419            7 :     return (m_fndecl == other.m_fndecl
    1420            7 :             && m_reg == other.m_reg
    1421           14 :             && same_tree_p (m_var_decl, other.m_var_decl));
    1422              :   }
    1423              : 
    1424           14 :   int get_controlling_option () const final override
    1425              :   {
    1426           14 :     return OPT_Wanalyzer_putenv_of_auto_var;
    1427              :   }
    1428              : 
    1429            7 :   bool emit (diagnostic_emission_context &ctxt) final override
    1430              :   {
    1431            7 :     auto_diagnostic_group d;
    1432              : 
    1433              :     /* SEI CERT C Coding Standard: "POS34-C. Do not call putenv() with a
    1434              :        pointer to an automatic variable as the argument".  */
    1435            7 :     diagnostics::metadata::precanned_rule
    1436            7 :       rule ("POS34-C", "https://wiki.sei.cmu.edu/confluence/x/6NYxBQ");
    1437            7 :     ctxt.add_rule (rule);
    1438              : 
    1439            7 :     bool warned;
    1440            7 :     if (m_var_decl)
    1441            6 :       warned = ctxt.warn ("%qE on a pointer to automatic variable %qE",
    1442              :                           m_fndecl, m_var_decl);
    1443              :     else
    1444            1 :       warned = ctxt.warn ("%qE on a pointer to an on-stack buffer",
    1445              :                           m_fndecl);
    1446            7 :     if (warned)
    1447              :       {
    1448            7 :         if (m_var_decl)
    1449            6 :           inform (DECL_SOURCE_LOCATION (m_var_decl),
    1450              :                   "%qE declared on stack here", m_var_decl);
    1451            7 :         inform (ctxt.get_location (), "perhaps use %qs rather than %qE",
    1452              :                 "setenv", m_fndecl);
    1453              :       }
    1454              : 
    1455           14 :     return warned;
    1456            7 :   }
    1457              : 
    1458              :   bool
    1459           14 :   describe_final_event (pretty_printer &pp,
    1460              :                         const evdesc::final_event &) final override
    1461              :   {
    1462           14 :     if (m_var_decl)
    1463           12 :       pp_printf  (&pp,
    1464              :                   "%qE on a pointer to automatic variable %qE",
    1465              :                   m_fndecl, m_var_decl);
    1466              :     else
    1467            2 :       pp_printf  (&pp,
    1468              :                   "%qE on a pointer to an on-stack buffer",
    1469              :                   m_fndecl);
    1470           14 :     return true;
    1471              :   }
    1472              : 
    1473           14 :   void mark_interesting_stuff (interesting_t *interest) final override
    1474              :   {
    1475           14 :     if (!m_var_decl)
    1476            2 :       interest->add_region_creation (m_reg->get_base_region ());
    1477           14 :   }
    1478              : 
    1479              : private:
    1480              :   tree m_fndecl; // non-NULL
    1481              :   const region *m_reg; // non-NULL
    1482              :   tree m_var_decl; // could be NULL
    1483              : };
    1484              : 
    1485        20538 : class kf_atoi_family: public known_function
    1486              : {
    1487              : public:
    1488           41 :   bool matches_call_types_p (const call_details &cd) const final override
    1489              :   {
    1490           41 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
    1491              :   }
    1492              : 
    1493            6 :   void impl_call_pre (const call_details &cd) const final override
    1494              :   {
    1495              :     /* atoi expects a valid, null-terminated string. */
    1496            6 :     cd.check_for_null_terminated_string_arg (0, false, nullptr);
    1497              :     
    1498              :     /* atoi returns an integer, but we don't know what it is statically. 
    1499              :        Tell the analyzer to assume it returns a generic, unknown value. */
    1500            6 :     cd.set_any_lhs_with_defaults ();
    1501            6 :   }
    1502              : };
    1503              : 
    1504              : /* Handler for calls to "getenv".
    1505              :      char *getenv (const char *name);
    1506              : 
    1507              :    Returns either NULL (if the environment variable is not found),
    1508              :    or a pointer to the value string.  */
    1509              : 
    1510         3423 : class kf_getenv : public known_function
    1511              : {
    1512              : public:
    1513           63 :   bool matches_call_types_p (const call_details &cd) const final override
    1514              :   {
    1515           63 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
    1516              :   }
    1517              : 
    1518            9 :   void impl_call_pre (const call_details &cd) const final override
    1519              :   {
    1520            9 :     cd.check_for_null_terminated_string_arg (0);
    1521            9 :   }
    1522              : 
    1523              :   void impl_call_post (const call_details &cd) const final override;
    1524              : };
    1525              : 
    1526              : void
    1527            9 : kf_getenv::impl_call_post (const call_details &cd) const
    1528              : {
    1529            0 :   class getenv_call_info : public call_info
    1530              :   {
    1531              :   public:
    1532           18 :     getenv_call_info (const call_details &cd, bool found)
    1533           18 :     : call_info (cd), m_found (found)
    1534              :     {
    1535              :     }
    1536              : 
    1537           74 :     void print_desc (pretty_printer &pp) const final override
    1538              :     {
    1539           74 :       if (m_found)
    1540           70 :         pp_printf (&pp,
    1541              :                    "when %qE returns non-NULL",
    1542              :                    get_fndecl ());
    1543              :       else
    1544            4 :         pp_printf (&pp,
    1545              :                    "when %qE returns NULL",
    1546              :                    get_fndecl ());
    1547           74 :     }
    1548              : 
    1549          509 :     bool update_model (region_model *model,
    1550              :                        const exploded_edge *,
    1551              :                        region_model_context *ctxt) const final override
    1552              :     {
    1553          509 :       const call_details cd (get_call_details (model, ctxt));
    1554          509 :       if (tree lhs_type = cd.get_lhs_type ())
    1555              :         {
    1556          507 :           region_model_manager *mgr = model->get_manager ();
    1557          507 :           const svalue *result;
    1558          507 :           if (m_found)
    1559              :             {
    1560              :               /* Return a conjured non-NULL pointer.  */
    1561          269 :               result
    1562          269 :                 = mgr->get_or_create_conjured_svalue (lhs_type,
    1563          269 :                                                        &cd.get_call_stmt (),
    1564              :                                                        cd.get_lhs_region (),
    1565          269 :                                                        conjured_purge (model,
    1566          269 :                                                                        ctxt));
    1567          269 :               const svalue *null_ptr
    1568          269 :                 = mgr->get_or_create_int_cst (lhs_type, 0);
    1569          269 :               model->add_constraint (result, NE_EXPR, null_ptr, ctxt);
    1570              :             }
    1571              :           else
    1572          238 :             result = mgr->get_or_create_int_cst (lhs_type, 0);
    1573          507 :           cd.maybe_set_lhs (result);
    1574              :         }
    1575          509 :       return true;
    1576              :     }
    1577              :   private:
    1578              :     bool m_found;
    1579              :   };
    1580              : 
    1581              :   /* Body of kf_getenv::impl_call_post.  */
    1582            9 :   if (cd.get_ctxt ())
    1583              :     {
    1584            9 :       cd.get_ctxt ()->bifurcate
    1585            9 :         (std::make_unique<getenv_call_info> (cd, false));
    1586            9 :       cd.get_ctxt ()->bifurcate
    1587            9 :         (std::make_unique<getenv_call_info> (cd, true));
    1588            9 :       cd.get_ctxt ()->terminate_path ();
    1589              :     }
    1590            9 : }
    1591              : 
    1592              : /* Handler for calls to "putenv".
    1593              : 
    1594              :    In theory we could try to model the state of the environment variables
    1595              :    for the process; for now we merely complain about putenv of regions
    1596              :    on the stack.  */
    1597              : 
    1598         3423 : class kf_putenv : public known_function
    1599              : {
    1600              : public:
    1601          118 :   bool matches_call_types_p (const call_details &cd) const final override
    1602              :   {
    1603          118 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
    1604              :   }
    1605              : 
    1606           17 :   void impl_call_pre (const call_details &cd) const final override
    1607              :   {
    1608           17 :     tree fndecl = cd.get_fndecl_for_call ();
    1609           17 :     gcc_assert (fndecl);
    1610           17 :     region_model_context *ctxt = cd.get_ctxt ();
    1611           17 :     region_model *model = cd.get_model ();
    1612           17 :     model->check_for_null_terminated_string_arg (cd, 0);
    1613           17 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
    1614           17 :     const region *reg
    1615           17 :       = model->deref_rvalue (ptr_sval, cd.get_arg_tree (0), ctxt);
    1616           17 :     store_manager *store_mgr = model->get_manager ()->get_store_manager ();
    1617           17 :     model->get_store ()->mark_as_escaped (*store_mgr, reg->get_base_region ());
    1618           17 :     enum memory_space mem_space = reg->get_memory_space ();
    1619           17 :     switch (mem_space)
    1620              :       {
    1621            0 :       default:
    1622            0 :         gcc_unreachable ();
    1623              :       case MEMSPACE_UNKNOWN:
    1624              :       case MEMSPACE_CODE:
    1625              :       case MEMSPACE_GLOBALS:
    1626              :       case MEMSPACE_HEAP:
    1627              :       case MEMSPACE_READONLY_DATA:
    1628              :         break;
    1629            7 :       case MEMSPACE_STACK:
    1630            7 :         if (ctxt)
    1631            7 :           ctxt->warn (std::make_unique<putenv_of_auto_var> (fndecl, reg));
    1632              :         break;
    1633              :       }
    1634           17 :     cd.set_any_lhs_with_defaults ();
    1635           17 :   }
    1636              : };
    1637              : 
    1638              : /* Handler for "realloc":
    1639              : 
    1640              :      void *realloc(void *ptr, size_t size);
    1641              : 
    1642              :    realloc(3) is awkward, since it has various different outcomes
    1643              :    that are best modelled as separate exploded nodes/edges.
    1644              : 
    1645              :    We first check for sm-state, in
    1646              :    malloc_state_machine::on_realloc_call, so that we
    1647              :    can complain about issues such as realloc of a non-heap
    1648              :    pointer, and terminate the path for such cases (and issue
    1649              :    the complaints at the call's exploded node).
    1650              : 
    1651              :    Assuming that these checks pass, we split the path here into
    1652              :    three special cases (and terminate the "standard" path):
    1653              :    (A) failure, returning NULL
    1654              :    (B) success, growing the buffer in-place without moving it
    1655              :    (C) success, allocating a new buffer, copying the content
    1656              :    of the old buffer to it, and freeing the old buffer.
    1657              : 
    1658              :    Each of these has a custom_edge_info subclass, which updates
    1659              :    the region_model and sm-state of the destination state.  */
    1660              : 
    1661        10269 : class kf_realloc : public builtin_known_function
    1662              : {
    1663              : public:
    1664         1980 :   bool matches_call_types_p (const call_details &cd) const final override
    1665              :   {
    1666         1980 :     return (cd.num_args () == 2
    1667         1980 :             && cd.arg_is_pointer_p (0)
    1668         3960 :             && cd.arg_is_size_p (1));
    1669              :   }
    1670              : 
    1671         2004 :   enum built_in_function builtin_code () const final override
    1672              :   {
    1673         2004 :     return BUILT_IN_REALLOC;
    1674              :   }
    1675              : 
    1676              :   void impl_call_post (const call_details &cd) const final override;
    1677              : };
    1678              : 
    1679              : void
    1680          310 : kf_realloc::impl_call_post (const call_details &cd) const
    1681              : {
    1682              :   /* Three custom subclasses of custom_edge_info, for handling the various
    1683              :      outcomes of "realloc".  */
    1684              : 
    1685              :   /* Concrete custom_edge_info: a realloc call that fails, returning NULL.  */
    1686            0 :   class failure : public failed_call_info
    1687              :   {
    1688              :   public:
    1689          310 :     failure (const call_details &cd)
    1690          310 :     : failed_call_info (cd)
    1691              :     {
    1692              :     }
    1693              : 
    1694          374 :     bool update_model (region_model *model,
    1695              :                        const exploded_edge *,
    1696              :                        region_model_context *ctxt) const final override
    1697              :     {
    1698              :       /* Return NULL; everything else is unchanged.  */
    1699          374 :       const call_details cd (get_call_details (model, ctxt));
    1700          374 :       region_model_manager *mgr = cd.get_manager ();
    1701          374 :       if (cd.get_lhs_type ())
    1702              :         {
    1703          365 :           const svalue *zero
    1704          365 :             = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
    1705          365 :           model->set_value (cd.get_lhs_region (),
    1706              :                             zero,
    1707              :                             cd.get_ctxt ());
    1708              :         }
    1709          374 :       return true;
    1710              :     }
    1711              :   };
    1712              : 
    1713              :   /* Concrete custom_edge_info: a realloc call that succeeds, growing
    1714              :      the existing buffer without moving it.  */
    1715            0 :   class success_no_move : public call_info
    1716              :   {
    1717              :   public:
    1718          310 :     success_no_move (const call_details &cd)
    1719          310 :     : call_info (cd)
    1720              :     {
    1721              :     }
    1722              : 
    1723            8 :     void print_desc (pretty_printer &pp) const final override
    1724              :     {
    1725            8 :       pp_printf (&pp,
    1726              :                  "when %qE succeeds, without moving buffer",
    1727              :                  get_fndecl ());
    1728            8 :     }
    1729              : 
    1730          343 :     bool update_model (region_model *model,
    1731              :                        const exploded_edge *,
    1732              :                        region_model_context *ctxt) const final override
    1733              :     {
    1734              :       /* Update size of buffer and return the ptr unchanged.  */
    1735          343 :       const call_details cd (get_call_details (model, ctxt));
    1736          343 :       region_model_manager *mgr = cd.get_manager ();
    1737          343 :       const svalue *ptr_sval = cd.get_arg_svalue (0);
    1738          343 :       const svalue *size_sval = cd.get_arg_svalue (1);
    1739              : 
    1740              :       /* We can only grow in place with a non-NULL pointer.  */
    1741          343 :       {
    1742          343 :         const svalue *null_ptr
    1743          343 :           = mgr->get_or_create_int_cst (ptr_sval->get_type (), 0);
    1744          343 :         if (!model->add_constraint (ptr_sval, NE_EXPR, null_ptr,
    1745              :                                     cd.get_ctxt ()))
    1746              :           return false;
    1747              :       }
    1748              : 
    1749          337 :       if (const region *buffer_reg = model->deref_rvalue (ptr_sval, NULL_TREE,
    1750              :                                                           ctxt))
    1751          337 :         if (compat_types_p (size_sval->get_type (), size_type_node))
    1752          337 :           model->set_dynamic_extents (buffer_reg, size_sval, ctxt);
    1753          337 :       if (cd.get_lhs_region ())
    1754              :         {
    1755          328 :           model->set_value (cd.get_lhs_region (), ptr_sval, cd.get_ctxt ());
    1756          328 :           const svalue *zero
    1757          328 :             = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
    1758          328 :           return model->add_constraint (ptr_sval, NE_EXPR, zero, ctxt);
    1759              :         }
    1760              :       else
    1761              :         return true;
    1762              :     }
    1763              :   };
    1764              : 
    1765              :   /* Concrete custom_edge_info: a realloc call that succeeds, freeing
    1766              :      the existing buffer and moving the content to a freshly allocated
    1767              :      buffer.  */
    1768            0 :   class success_with_move : public call_info
    1769              :   {
    1770              :   public:
    1771          310 :     success_with_move (const call_details &cd)
    1772          310 :     : call_info (cd)
    1773              :     {
    1774              :     }
    1775              : 
    1776           72 :     void print_desc (pretty_printer &pp) const final override
    1777              :     {
    1778           72 :       pp_printf (&pp,
    1779              :                  "when %qE succeeds, moving buffer",
    1780              :                  get_fndecl ());
    1781           72 :     }
    1782          430 :     bool update_model (region_model *model,
    1783              :                        const exploded_edge *,
    1784              :                        region_model_context *ctxt) const final override
    1785              :     {
    1786          430 :       const call_details cd (get_call_details (model, ctxt));
    1787          430 :       region_model_manager *mgr = cd.get_manager ();
    1788          430 :       const svalue *old_ptr_sval = cd.get_arg_svalue (0);
    1789          430 :       const svalue *new_size_sval = cd.get_arg_svalue (1);
    1790              : 
    1791              :       /* Create the new region.  */
    1792          430 :       const region *new_reg
    1793          430 :         = model->get_or_create_region_for_heap_alloc (new_size_sval, ctxt);
    1794          430 :       const svalue *new_ptr_sval
    1795          430 :         = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
    1796          430 :       if (!model->add_constraint (new_ptr_sval, NE_EXPR, old_ptr_sval,
    1797              :                                   cd.get_ctxt ()))
    1798              :         return false;
    1799              : 
    1800          430 :       if (cd.get_lhs_type ())
    1801          421 :         cd.maybe_set_lhs (new_ptr_sval);
    1802              : 
    1803          430 :       if (const region *freed_reg = model->deref_rvalue (old_ptr_sval,
    1804              :                                                          NULL_TREE, ctxt))
    1805              :         {
    1806              :           /* Copy the data.  */
    1807          430 :           const svalue *old_size_sval = model->get_dynamic_extents (freed_reg);
    1808          430 :           if (old_size_sval)
    1809              :             {
    1810          187 :               const svalue *copied_size_sval
    1811          187 :                 = get_copied_size (model, old_size_sval, new_size_sval);
    1812          187 :               const region *copied_old_reg
    1813          187 :                 = mgr->get_sized_region (freed_reg, nullptr, copied_size_sval);
    1814          187 :               const svalue *buffer_content_sval
    1815          187 :                 = model->get_store_value (copied_old_reg, cd.get_ctxt ());
    1816          187 :               const region *copied_new_reg
    1817          187 :                 = mgr->get_sized_region (new_reg, nullptr, copied_size_sval);
    1818          187 :               model->set_value (copied_new_reg, buffer_content_sval,
    1819              :                                 cd.get_ctxt ());
    1820              :             }
    1821              :           else
    1822              :             {
    1823              :               /* We don't know how big the old region was;
    1824              :                  mark the new region as having been touched to avoid uninit
    1825              :                  issues.  */
    1826          243 :               model->mark_region_as_unknown (new_reg, cd.get_uncertainty ());
    1827              :             }
    1828              : 
    1829              :           /* Free the old region, so that pointers to the old buffer become
    1830              :              invalid.  */
    1831              : 
    1832              :           /* If the ptr points to an underlying heap region, delete it,
    1833              :              poisoning pointers.  */
    1834          430 :           model->unbind_region_and_descendents (freed_reg, poison_kind::freed);
    1835          430 :           model->unset_dynamic_extents (freed_reg);
    1836              :         }
    1837              : 
    1838              :       /* Update the sm-state: mark the old_ptr_sval as "freed",
    1839              :          and the new_ptr_sval as "nonnull".  */
    1840          430 :       model->on_realloc_with_move (cd, old_ptr_sval, new_ptr_sval);
    1841              : 
    1842          430 :       if (cd.get_lhs_type ())
    1843              :         {
    1844          421 :           const svalue *zero
    1845          421 :             = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
    1846          421 :           return model->add_constraint (new_ptr_sval, NE_EXPR, zero,
    1847          421 :                                         cd.get_ctxt ());
    1848              :         }
    1849              :       else
    1850              :         return true;
    1851              :     }
    1852              : 
    1853              :   private:
    1854              :     /* Return the lesser of OLD_SIZE_SVAL and NEW_SIZE_SVAL.
    1855              :        If unknown, OLD_SIZE_SVAL is returned.  */
    1856          187 :     const svalue *get_copied_size (region_model *model,
    1857              :                                    const svalue *old_size_sval,
    1858              :                                    const svalue *new_size_sval) const
    1859              :     {
    1860          187 :       tristate res
    1861          187 :         = model->eval_condition (old_size_sval, GT_EXPR, new_size_sval);
    1862          187 :       switch (res.get_value ())
    1863              :         {
    1864              :         case tristate::TS_TRUE:
    1865              :           return new_size_sval;
    1866          143 :         case tristate::TS_FALSE:
    1867          143 :         case tristate::TS_UNKNOWN:
    1868          143 :           return old_size_sval;
    1869            0 :         default:
    1870            0 :           gcc_unreachable ();
    1871              :         }
    1872              :     }
    1873              :   };
    1874              : 
    1875              :   /* Body of kf_realloc::impl_call_post.  */
    1876              : 
    1877          310 :   if (cd.get_ctxt ())
    1878              :     {
    1879          310 :       cd.get_ctxt ()->bifurcate (std::make_unique<failure> (cd));
    1880          310 :       cd.get_ctxt ()->bifurcate (std::make_unique<success_no_move> (cd));
    1881          310 :       cd.get_ctxt ()->bifurcate (std::make_unique<success_with_move> (cd));
    1882          310 :       cd.get_ctxt ()->terminate_path ();
    1883              :     }
    1884          310 : }
    1885              : 
    1886              : /* Handler for "strchr" and "__builtin_strchr".  */
    1887              : 
    1888         6846 : class kf_strchr : public builtin_known_function
    1889              : {
    1890              : public:
    1891          153 :   bool matches_call_types_p (const call_details &cd) const final override
    1892              :   {
    1893          153 :     return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
    1894              :   }
    1895           22 :   void impl_call_pre (const call_details &cd) const final override
    1896              :   {
    1897           22 :     cd.check_for_null_terminated_string_arg (0);
    1898           22 :   }
    1899              : 
    1900          176 :   enum built_in_function builtin_code () const final override
    1901              :   {
    1902          176 :     return BUILT_IN_STRCHR;
    1903              :   }
    1904              :   void impl_call_post (const call_details &cd) const final override;
    1905              : };
    1906              : 
    1907              : void
    1908           21 : kf_strchr::impl_call_post (const call_details &cd) const
    1909              : {
    1910            0 :   class strchr_call_info : public call_info
    1911              :   {
    1912              :   public:
    1913           42 :     strchr_call_info (const call_details &cd, bool found)
    1914           42 :     : call_info (cd), m_found (found)
    1915              :     {
    1916              :     }
    1917              : 
    1918           38 :     void print_desc (pretty_printer &pp) const final override
    1919              :     {
    1920           38 :       if (m_found)
    1921           18 :         pp_printf (&pp,
    1922              :                    "when %qE returns non-NULL",
    1923              :                    get_fndecl ());
    1924              :       else
    1925           20 :         pp_printf (&pp,
    1926              :                    "when %qE returns NULL",
    1927              :                    get_fndecl ());
    1928           38 :     }
    1929              : 
    1930           84 :     bool update_model (region_model *model,
    1931              :                        const exploded_edge *,
    1932              :                        region_model_context *ctxt) const final override
    1933              :     {
    1934           84 :       const call_details cd (get_call_details (model, ctxt));
    1935           84 :       if (tree lhs_type = cd.get_lhs_type ())
    1936              :         {
    1937           82 :           region_model_manager *mgr = model->get_manager ();
    1938           82 :           const svalue *result;
    1939           82 :           if (m_found)
    1940              :             {
    1941           40 :               const svalue *str_sval = cd.get_arg_svalue (0);
    1942           40 :               const region *str_reg
    1943           40 :                 = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
    1944              :                                        cd.get_ctxt ());
    1945              :               /* We want str_sval + OFFSET for some unknown OFFSET.
    1946              :                  Use a conjured_svalue to represent the offset,
    1947              :                  using the str_reg as the id of the conjured_svalue.  */
    1948           40 :               const svalue *offset
    1949           40 :                 = mgr->get_or_create_conjured_svalue (size_type_node,
    1950           40 :                                                       &cd.get_call_stmt (),
    1951              :                                                       str_reg,
    1952           40 :                                                       conjured_purge (model,
    1953           40 :                                                                       ctxt));
    1954           40 :               result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR,
    1955              :                                                  str_sval, offset);
    1956              :             }
    1957              :           else
    1958           42 :             result = mgr->get_or_create_int_cst (lhs_type, 0);
    1959           82 :           cd.maybe_set_lhs (result);
    1960              :         }
    1961           84 :       return true;
    1962              :     }
    1963              :   private:
    1964              :     bool m_found;
    1965              :   };
    1966              : 
    1967              :   /* Body of kf_strchr::impl_call_post.  */
    1968           21 :   if (cd.get_ctxt ())
    1969              :     {
    1970           21 :       cd.get_ctxt ()->bifurcate (std::make_unique<strchr_call_info> (cd, false));
    1971           21 :       cd.get_ctxt ()->bifurcate (std::make_unique<strchr_call_info> (cd, true));
    1972           21 :       cd.get_ctxt ()->terminate_path ();
    1973              :     }
    1974           21 : }
    1975              : 
    1976              : /* Handler for "sprintf".
    1977              :      int sprintf(char *str, const char *format, ...);
    1978              : */
    1979              : 
    1980         6846 : class kf_sprintf : public builtin_known_function
    1981              : {
    1982              : public:
    1983         3223 :   bool matches_call_types_p (const call_details &cd) const final override
    1984              :   {
    1985         3223 :     return (cd.num_args () >= 2
    1986         3223 :             && cd.arg_is_pointer_p (0)
    1987         6446 :             && cd.arg_is_pointer_p (1));
    1988              :   }
    1989              : 
    1990          640 :   enum built_in_function builtin_code () const final override
    1991              :   {
    1992          640 :     return BUILT_IN_SPRINTF;
    1993              :   }
    1994              : 
    1995         1416 :   void impl_call_pre (const call_details &cd) const final override
    1996              :   {
    1997              :     /* For now, merely assume that the destination buffer gets set to a
    1998              :        new svalue.  */
    1999         1416 :     region_model *model = cd.get_model ();
    2000         1416 :     region_model_context *ctxt = cd.get_ctxt ();
    2001         1416 :     const svalue *dst_ptr = cd.get_arg_svalue (0);
    2002         1416 :     const region *dst_reg
    2003         1416 :       = model->deref_rvalue (dst_ptr, cd.get_arg_tree (0), ctxt);
    2004              :     /* Restrict the region we consider to be affected to the valid capacity
    2005              :        so that we don't trigger buffer overflow false positives.  */
    2006         1416 :     const svalue *capacity = model->get_capacity (dst_reg);
    2007         1416 :     dst_reg = model->get_manager ()->get_sized_region (dst_reg,
    2008              :                                                        NULL_TREE,
    2009              :                                                        capacity);
    2010         1416 :     const svalue *content = cd.get_or_create_conjured_svalue (dst_reg);
    2011         1416 :     model->set_value (dst_reg, content, ctxt);
    2012         1416 :     cd.set_any_lhs_with_defaults ();
    2013         1416 :   }
    2014              : };
    2015              : 
    2016              : /* Handler for "__builtin_stack_restore".  */
    2017              : 
    2018         3423 : class kf_stack_restore : public pure_known_function_with_default_return
    2019              : {
    2020              : public:
    2021            0 :   bool matches_call_types_p (const call_details &) const final override
    2022              :   {
    2023            0 :     return true;
    2024              :   }
    2025              : 
    2026              :   /* Currently a no-op.  */
    2027              : };
    2028              : 
    2029              : /* Handler for "__builtin_stack_save".  */
    2030              : 
    2031         3423 : class kf_stack_save : public pure_known_function_with_default_return
    2032              : {
    2033              : public:
    2034            0 :   bool matches_call_types_p (const call_details &) const final override
    2035              :   {
    2036            0 :     return true;
    2037              :   }
    2038              : 
    2039              :   /* Currently a no-op.  */
    2040              : };
    2041              : 
    2042              : /* Handler for "__builtin_eh_pointer".  */
    2043              : 
    2044         3423 : class kf_eh_pointer : public builtin_known_function
    2045              : {
    2046              : public:
    2047            0 :   bool matches_call_types_p (const call_details &) const final override
    2048              :   {
    2049            0 :     return true;
    2050              :   }
    2051              : 
    2052          992 :   enum built_in_function builtin_code () const final override
    2053              :   {
    2054          992 :     return BUILT_IN_EH_POINTER;
    2055              :   }
    2056              : 
    2057          271 :   void impl_call_pre (const call_details &cd) const final override
    2058              :   {
    2059          271 :     cd.set_any_lhs_with_defaults ();
    2060          271 :   }
    2061              : };
    2062              : 
    2063              : /* Handler for "strcat" and "__builtin_strcat_chk".  */
    2064              : 
    2065              : class kf_strcat : public builtin_known_function
    2066              : {
    2067              : public:
    2068        17115 :   kf_strcat (unsigned int num_args, bool chk_variant)
    2069        17115 :     : m_num_args (num_args),
    2070        17115 :       m_chk_variant (chk_variant) {}
    2071          395 :   bool matches_call_types_p (const call_details &cd) const final override
    2072              :   {
    2073          395 :     return (cd.num_args () == m_num_args
    2074          395 :             && cd.arg_is_pointer_p (0)
    2075          790 :             && cd.arg_is_pointer_p (1));
    2076              :   }
    2077              : 
    2078          456 :   enum built_in_function builtin_code () const final override
    2079              :   {
    2080          456 :     return m_chk_variant ? BUILT_IN_STRCAT_CHK : BUILT_IN_STRCAT;
    2081              :   }
    2082              : 
    2083           57 :   void impl_call_pre (const call_details &cd) const final override
    2084              :   {
    2085           57 :     region_model *model = cd.get_model ();
    2086           57 :     region_model_manager *mgr = cd.get_manager ();
    2087              : 
    2088           57 :     const svalue *dest_sval = cd.get_arg_svalue (0);
    2089           57 :     const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0),
    2090              :                                                   cd.get_ctxt ());
    2091              : 
    2092           57 :     const svalue *dst_strlen_sval
    2093           57 :       = cd.check_for_null_terminated_string_arg (0, false, nullptr);
    2094           57 :     if (!dst_strlen_sval)
    2095              :       {
    2096            2 :         if (cd.get_ctxt ())
    2097            2 :           cd.get_ctxt ()->terminate_path ();
    2098            4 :         return;
    2099              :       }
    2100              : 
    2101           55 :     const svalue *bytes_to_copy;
    2102           55 :     const svalue *num_src_bytes_read_sval
    2103           55 :       = cd.check_for_null_terminated_string_arg (1, true, &bytes_to_copy);
    2104           55 :     if (!num_src_bytes_read_sval)
    2105              :       {
    2106            2 :         if (cd.get_ctxt ())
    2107            2 :           cd.get_ctxt ()->terminate_path ();
    2108            2 :         return;
    2109              :       }
    2110              : 
    2111           53 :     cd.maybe_set_lhs (dest_sval);
    2112           53 :     cd.complain_about_overlap (0, 1, num_src_bytes_read_sval);
    2113              : 
    2114           53 :     const region *offset_reg
    2115           53 :       = mgr->get_offset_region (dest_reg, NULL_TREE, dst_strlen_sval);
    2116           53 :     model->write_bytes (offset_reg,
    2117              :                         num_src_bytes_read_sval,
    2118              :                         bytes_to_copy,
    2119              :                         cd.get_ctxt ());
    2120              :   }
    2121              : 
    2122              : private:
    2123              :   unsigned int m_num_args;
    2124              :   const bool m_chk_variant;
    2125              : };
    2126              : 
    2127              : /* Handler for "strcpy" and "__builtin_strcpy_chk".  */
    2128              : 
    2129              : class kf_strcpy : public builtin_known_function
    2130              : {
    2131              : public:
    2132        17115 :   kf_strcpy (unsigned int num_args, bool chk_variant)
    2133        17115 :     : m_num_args (num_args),
    2134        17115 :       m_chk_variant (chk_variant) {}
    2135         2053 :   bool matches_call_types_p (const call_details &cd) const final override
    2136              :   {
    2137         2053 :     return (cd.num_args () == m_num_args
    2138         2053 :             && cd.arg_is_pointer_p (0)
    2139         4106 :             && cd.arg_is_pointer_p (1));
    2140              :   }
    2141          656 :   enum built_in_function builtin_code () const final override
    2142              :   {
    2143          656 :     return m_chk_variant ? BUILT_IN_STRCPY_CHK : BUILT_IN_STRCPY;
    2144              :   }
    2145              :   void impl_call_pre (const call_details &cd) const final override;
    2146              : 
    2147              : private:
    2148              :   unsigned int m_num_args;
    2149              :   const bool m_chk_variant;
    2150              : };
    2151              : 
    2152              : void
    2153          823 : kf_strcpy::impl_call_pre (const call_details &cd) const
    2154              : {
    2155          823 :   region_model *model = cd.get_model ();
    2156          823 :   region_model_context *ctxt = cd.get_ctxt ();
    2157              : 
    2158          823 :   const svalue *dest_sval = cd.get_arg_svalue (0);
    2159          823 :   const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0),
    2160              :                                                     ctxt);
    2161              :   /* strcpy returns the initial param.  */
    2162          823 :   cd.maybe_set_lhs (dest_sval);
    2163              : 
    2164          823 :   const svalue *bytes_to_copy = nullptr;
    2165         1646 :   if (const svalue *num_bytes_read_sval
    2166          823 :       = cd.check_for_null_terminated_string_arg (1, true, &bytes_to_copy))
    2167              :     {
    2168          820 :       gcc_assert (bytes_to_copy);
    2169          820 :       cd.complain_about_overlap (0, 1, num_bytes_read_sval);
    2170          820 :       model->write_bytes (dest_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
    2171              :     }
    2172              :   else
    2173              :     {
    2174            3 :       if (cd.get_ctxt ())
    2175            3 :         cd.get_ctxt ()->terminate_path ();
    2176              :     }
    2177          823 : }
    2178              : 
    2179              : /* Handler for "strdup" and "__builtin_strdup".  */
    2180              : 
    2181         6846 : class kf_strdup : public builtin_known_function
    2182              : {
    2183              : public:
    2184         1456 :   bool matches_call_types_p (const call_details &cd) const final override
    2185              :   {
    2186         1456 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
    2187              :   }
    2188         1326 :   enum built_in_function builtin_code () const final override
    2189              :   {
    2190         1326 :     return BUILT_IN_STRDUP;
    2191              :   }
    2192          287 :   void impl_call_pre (const call_details &cd) const final override
    2193              :   {
    2194          287 :     region_model *model = cd.get_model ();
    2195          287 :     region_model_context *ctxt = cd.get_ctxt ();
    2196          287 :     region_model_manager *mgr = cd.get_manager ();
    2197          287 :     const svalue *bytes_to_copy;
    2198          574 :     if (const svalue *num_bytes_read_sval
    2199          287 :         = cd.check_for_null_terminated_string_arg (0, true, &bytes_to_copy))
    2200              :       {
    2201          285 :         const region *new_reg
    2202          285 :           = model->get_or_create_region_for_heap_alloc (num_bytes_read_sval,
    2203              :                                                         ctxt);
    2204          285 :         model->write_bytes (new_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
    2205          285 :         if (cd.get_lhs_type ())
    2206              :           {
    2207          285 :             const svalue *ptr_sval
    2208          285 :               = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
    2209          285 :             cd.maybe_set_lhs (ptr_sval);
    2210              :           }
    2211              :       }
    2212              :     else
    2213              :       {
    2214            2 :         if (ctxt)
    2215            2 :           ctxt->terminate_path ();
    2216              :       }
    2217          287 :   }
    2218              : };
    2219              : 
    2220              : /* Handler for "strlen" and for "__analyzer_get_strlen".  */
    2221              : 
    2222        13692 : class kf_strlen : public builtin_known_function
    2223              : {
    2224              : public:
    2225        10484 :   bool matches_call_types_p (const call_details &cd) const final override
    2226              :   {
    2227        10484 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
    2228              :   }
    2229         6816 :   enum built_in_function builtin_code () const final override
    2230              :   {
    2231         6816 :     return BUILT_IN_STRLEN;
    2232              :   }
    2233              : 
    2234         3116 :   void impl_call_pre (const call_details &cd) const final override
    2235              :   {
    2236         6232 :     if (const svalue *strlen_sval
    2237         3116 :           = cd.check_for_null_terminated_string_arg (0, false, nullptr))
    2238         3098 :       if (strlen_sval->get_kind () != SK_UNKNOWN)
    2239              :         {
    2240         2388 :           cd.maybe_set_lhs (strlen_sval);
    2241         2388 :           return;
    2242              :         }
    2243              : 
    2244              :     /* Use a conjured svalue.  */
    2245          728 :     cd.set_any_lhs_with_defaults ();
    2246              :   }
    2247              : };
    2248              : 
    2249              : /* Factory function, so that kf-analyzer.cc can use this class.  */
    2250              : 
    2251              : std::unique_ptr<known_function>
    2252         3423 : make_kf_strlen ()
    2253              : {
    2254         3423 :   return std::make_unique<kf_strlen> ();
    2255              : }
    2256              : 
    2257              : /* Handler for "strncpy" and "__builtin_strncpy".
    2258              :    See e.g. https://en.cppreference.com/w/c/string/byte/strncpy
    2259              : 
    2260              :      extern char *strncpy (char *dst, const char *src, size_t count);
    2261              : 
    2262              :    Handle this by splitting into two outcomes:
    2263              :    (a) truncated read from "src" of "count" bytes,
    2264              :        writing "count" bytes to "dst"
    2265              :    (b) read from "src" of up to (and including) the null terminator,
    2266              :        where the number of bytes read < "count" bytes,
    2267              :        writing those bytes to "dst", and zero-filling the rest,
    2268              :        up to "count".  */
    2269              : 
    2270        10269 : class kf_strncpy : public builtin_known_function
    2271              : {
    2272              : public:
    2273         1225 :   bool matches_call_types_p (const call_details &cd) const final override
    2274              :   {
    2275         1225 :     return (cd.num_args () == 3
    2276         1225 :             && cd.arg_is_pointer_p (0)
    2277         1225 :             && cd.arg_is_pointer_p (1)
    2278         2450 :             && cd.arg_is_integral_p (2));
    2279              :   }
    2280         1400 :   enum built_in_function builtin_code () const final override
    2281              :   {
    2282         1400 :     return BUILT_IN_STRNCPY;
    2283              :   }
    2284              :   void impl_call_post (const call_details &cd) const final override;
    2285              : };
    2286              : 
    2287              : void
    2288          175 : kf_strncpy::impl_call_post (const call_details &cd) const
    2289              : {
    2290            0 :   class strncpy_call_info : public call_info
    2291              :   {
    2292              :   public:
    2293          350 :     strncpy_call_info (const call_details &cd,
    2294              :                        const svalue *num_bytes_with_terminator_sval,
    2295              :                        bool truncated_read)
    2296          350 :     : call_info (cd),
    2297          350 :       m_num_bytes_with_terminator_sval (num_bytes_with_terminator_sval),
    2298          350 :       m_truncated_read (truncated_read)
    2299              :     {
    2300              :     }
    2301              : 
    2302           76 :     void print_desc (pretty_printer &pp) const final override
    2303              :     {
    2304           76 :       if (m_truncated_read)
    2305            4 :         pp_printf (&pp,
    2306              :                    "when %qE truncates the source string",
    2307              :                    get_fndecl ());
    2308              :       else
    2309           72 :         pp_printf (&pp,
    2310              :                    "when %qE copies the full source string",
    2311              :                    get_fndecl ());
    2312           76 :     }
    2313              : 
    2314          520 :     bool update_model (region_model *model,
    2315              :                        const exploded_edge *,
    2316              :                        region_model_context *ctxt) const final override
    2317              :     {
    2318          520 :       const call_details cd (get_call_details (model, ctxt));
    2319              : 
    2320          520 :       const svalue *dest_sval = cd.get_arg_svalue (0);
    2321          520 :       const region *dest_reg
    2322          520 :         = model->deref_rvalue (dest_sval, cd.get_arg_tree (0), ctxt);
    2323              : 
    2324          520 :       const svalue *src_sval = cd.get_arg_svalue (1);
    2325          520 :       const region *src_reg
    2326          520 :         = model->deref_rvalue (src_sval, cd.get_arg_tree (1), ctxt);
    2327              : 
    2328          520 :       const svalue *count_sval = cd.get_arg_svalue (2);
    2329              : 
    2330              :       /* strncpy returns the initial param.  */
    2331          520 :       cd.maybe_set_lhs (dest_sval);
    2332              : 
    2333          520 :       const svalue *num_bytes_read_sval;
    2334          520 :       if (m_truncated_read)
    2335              :         {
    2336              :           /* Truncated read.  */
    2337          243 :           num_bytes_read_sval = count_sval;
    2338              : 
    2339          243 :           if (m_num_bytes_with_terminator_sval)
    2340              :             {
    2341              :               /* The terminator is after the limit.  */
    2342          213 :               if (!model->add_constraint (m_num_bytes_with_terminator_sval,
    2343              :                                           GT_EXPR,
    2344              :                                           count_sval,
    2345              :                                           ctxt))
    2346              :                 return false;
    2347              :             }
    2348              :           else
    2349              :             {
    2350              :               /* We don't know where the terminator is, or if there is one.
    2351              :                  In theory we know that the first COUNT bytes are non-zero,
    2352              :                  but we don't have a way to record that constraint.  */
    2353              :             }
    2354              :         }
    2355              :       else
    2356              :         {
    2357              :           /* Full read of the src string before reaching the limit,
    2358              :              so there must be a terminator and it must be at or before
    2359              :              the limit.  */
    2360          277 :           if (m_num_bytes_with_terminator_sval)
    2361              :             {
    2362          247 :               if (!model->add_constraint (m_num_bytes_with_terminator_sval,
    2363              :                                           LE_EXPR,
    2364              :                                           count_sval,
    2365              :                                           ctxt))
    2366              :                 return false;
    2367          243 :               num_bytes_read_sval = m_num_bytes_with_terminator_sval;
    2368              : 
    2369              :               /* First, zero-fill the dest buffer.
    2370              :                  We don't need to do this for the truncation case, as
    2371              :                  this fully populates the dest buffer.  */
    2372          243 :               const region *sized_dest_reg
    2373          243 :                 = model->get_manager ()->get_sized_region (dest_reg,
    2374              :                                                            NULL_TREE,
    2375              :                                                            count_sval);
    2376          243 :               model->zero_fill_region (sized_dest_reg, ctxt);
    2377              :             }
    2378              :           else
    2379              :             {
    2380              :               /* Don't analyze this case; the other case will
    2381              :                  assume a "truncated" read up to the limit.  */
    2382              :               return false;
    2383              :             }
    2384              :         }
    2385              : 
    2386          472 :       gcc_assert (num_bytes_read_sval);
    2387              : 
    2388          472 :       const svalue *bytes_to_copy
    2389          472 :         = model->read_bytes (src_reg,
    2390              :                              cd.get_arg_tree (1),
    2391              :                              num_bytes_read_sval,
    2392              :                              ctxt);
    2393          472 :       cd.complain_about_overlap (0, 1, num_bytes_read_sval);
    2394          472 :       model->write_bytes (dest_reg,
    2395              :                           num_bytes_read_sval,
    2396              :                           bytes_to_copy,
    2397              :                           ctxt);
    2398              : 
    2399          472 :       return true;
    2400              :     }
    2401              :   private:
    2402              :     /* (strlen + 1) of the source string if it has a terminator,
    2403              :        or nullptr for the case where UB would happen before
    2404              :        finding any terminator.  */
    2405              :     const svalue *m_num_bytes_with_terminator_sval;
    2406              : 
    2407              :     /* true: if this is the outcome where the limit was reached before
    2408              :        the null terminator
    2409              :        false: if the null terminator was reached before the limit.  */
    2410              :     bool m_truncated_read;
    2411              :   };
    2412              : 
    2413              :   /* Body of kf_strncpy::impl_call_post.  */
    2414          175 :   if (cd.get_ctxt ())
    2415              :     {
    2416              :       /* First, scan for a null terminator as if there were no limit,
    2417              :          with a null ctxt so no errors are reported.  */
    2418          175 :       const region_model *model = cd.get_model ();
    2419          175 :       const svalue *ptr_arg_sval = cd.get_arg_svalue (1);
    2420          175 :       const region *buf_reg
    2421          175 :         = model->deref_rvalue (ptr_arg_sval, cd.get_arg_tree (1), nullptr);
    2422          175 :       const svalue *num_bytes_with_terminator_sval
    2423          175 :         = model->scan_for_null_terminator (buf_reg,
    2424              :                                            cd.get_arg_tree (1),
    2425              :                                            nullptr,
    2426          175 :                                            nullptr);
    2427          175 :       cd.get_ctxt ()->bifurcate
    2428          175 :         (std::make_unique<strncpy_call_info>
    2429          175 :            (cd, num_bytes_with_terminator_sval,
    2430          175 :             false));
    2431          175 :       cd.get_ctxt ()->bifurcate
    2432          175 :         (std::make_unique<strncpy_call_info>
    2433          175 :            (cd, num_bytes_with_terminator_sval,
    2434          175 :             true));
    2435          175 :       cd.get_ctxt ()->terminate_path ();
    2436              :     }
    2437          175 : };
    2438              : 
    2439              : /* Handler for "strndup" and "__builtin_strndup".  */
    2440              : 
    2441         6846 : class kf_strndup : public builtin_known_function
    2442              : {
    2443              : public:
    2444          124 :   bool matches_call_types_p (const call_details &cd) const final override
    2445              :   {
    2446          124 :     return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
    2447              :   }
    2448           84 :   enum built_in_function builtin_code () const final override
    2449              :   {
    2450           84 :     return BUILT_IN_STRNDUP;
    2451              :   }
    2452           34 :   void impl_call_pre (const call_details &cd) const final override
    2453              :   {
    2454           34 :     region_model *model = cd.get_model ();
    2455           34 :     region_model_manager *mgr = cd.get_manager ();
    2456              :     /* Ideally we'd get the size here, and simulate copying the bytes.  */
    2457           34 :     const region *new_reg
    2458           34 :       = model->get_or_create_region_for_heap_alloc (nullptr, cd.get_ctxt ());
    2459           34 :     model->mark_region_as_unknown (new_reg, nullptr);
    2460           34 :     if (cd.get_lhs_type ())
    2461              :       {
    2462           34 :         const svalue *ptr_sval
    2463           34 :           = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
    2464           34 :         cd.maybe_set_lhs (ptr_sval);
    2465              :       }
    2466           34 :   }
    2467              : };
    2468              : 
    2469              : /* Handler for "strstr" and "__builtin_strstr".
    2470              :      extern char *strstr (const char* str, const char* substr);
    2471              :    See e.g. https://en.cppreference.com/w/c/string/byte/strstr  */
    2472              : 
    2473         6846 : class kf_strstr : public builtin_known_function
    2474              : {
    2475              : public:
    2476          279 :   bool matches_call_types_p (const call_details &cd) const final override
    2477              :   {
    2478          279 :     return (cd.num_args () == 2
    2479          279 :             && cd.arg_is_pointer_p (0)
    2480          558 :             && cd.arg_is_pointer_p (1));
    2481              :   }
    2482          328 :   enum built_in_function builtin_code () const final override
    2483              :   {
    2484          328 :     return BUILT_IN_STRSTR;
    2485              :   }
    2486           41 :   void impl_call_pre (const call_details &cd) const final override
    2487              :   {
    2488           41 :     cd.check_for_null_terminated_string_arg (0);
    2489           41 :     cd.check_for_null_terminated_string_arg (1);
    2490           41 :   }
    2491              :   void impl_call_post (const call_details &cd) const final override;
    2492              : };
    2493              : 
    2494              : void
    2495           33 : kf_strstr::impl_call_post (const call_details &cd) const
    2496              : {
    2497            0 :   class strstr_call_info : public call_info
    2498              :   {
    2499              :   public:
    2500           66 :     strstr_call_info (const call_details &cd, bool found)
    2501           66 :     : call_info (cd), m_found (found)
    2502              :     {
    2503              :     }
    2504              : 
    2505            8 :     void print_desc (pretty_printer &pp) const final override
    2506              :     {
    2507            8 :       if (m_found)
    2508            0 :         pp_printf (&pp,
    2509              :                    "when %qE returns non-NULL",
    2510              :                    get_fndecl ());
    2511              :       else
    2512            8 :         pp_printf (&pp,
    2513              :                    "when %qE returns NULL",
    2514              :                    get_fndecl ());
    2515            8 :     }
    2516              : 
    2517          106 :     bool update_model (region_model *model,
    2518              :                        const exploded_edge *,
    2519              :                        region_model_context *ctxt) const final override
    2520              :     {
    2521          106 :       const call_details cd (get_call_details (model, ctxt));
    2522          106 :       if (tree lhs_type = cd.get_lhs_type ())
    2523              :         {
    2524          106 :           region_model_manager *mgr = model->get_manager ();
    2525          106 :           const svalue *result;
    2526          106 :           if (m_found)
    2527              :             {
    2528           33 :               const svalue *str_sval = cd.get_arg_svalue (0);
    2529           33 :               const region *str_reg
    2530           33 :                 = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
    2531              :                                        cd.get_ctxt ());
    2532              :               /* We want str_sval + OFFSET for some unknown OFFSET.
    2533              :                  Use a conjured_svalue to represent the offset,
    2534              :                  using the str_reg as the id of the conjured_svalue.  */
    2535           33 :               const svalue *offset
    2536           33 :                 = mgr->get_or_create_conjured_svalue (size_type_node,
    2537           33 :                                                       &cd.get_call_stmt (),
    2538              :                                                       str_reg,
    2539           33 :                                                       conjured_purge (model,
    2540           33 :                                                                       ctxt));
    2541           33 :               result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR,
    2542              :                                                  str_sval, offset);
    2543              :             }
    2544              :           else
    2545           73 :             result = mgr->get_or_create_int_cst (lhs_type, 0);
    2546          106 :           cd.maybe_set_lhs (result);
    2547              :         }
    2548          106 :       return true;
    2549              :     }
    2550              :   private:
    2551              :     bool m_found;
    2552              :   };
    2553              : 
    2554              :   /* Body of kf_strstr::impl_call_post.  */
    2555           33 :   if (cd.get_ctxt ())
    2556              :     {
    2557           33 :       cd.get_ctxt ()->bifurcate (std::make_unique<strstr_call_info> (cd, false));
    2558           33 :       cd.get_ctxt ()->bifurcate (std::make_unique<strstr_call_info> (cd, true));
    2559           33 :       cd.get_ctxt ()->terminate_path ();
    2560              :     }
    2561           33 : }
    2562              : 
    2563              : /* Handle calls to "strtok".
    2564              :    See e.g.
    2565              :      https://en.cppreference.com/w/c/string/byte/strtok
    2566              :      https://man7.org/linux/man-pages/man3/strtok.3.html  */
    2567              : 
    2568              : class kf_strtok : public known_function
    2569              : {
    2570              : public:
    2571              :   class undefined_behavior : public undefined_function_behavior
    2572              :   {
    2573              :   public:
    2574            8 :     undefined_behavior (const call_details &cd)
    2575            8 :     : undefined_function_behavior (cd)
    2576              :     {
    2577              :     }
    2578           12 :     int get_controlling_option () const final override
    2579              :     {
    2580           12 :       return OPT_Wanalyzer_undefined_behavior_strtok;
    2581              :     }
    2582              : 
    2583            4 :     bool emit (diagnostic_emission_context &ctxt) final override
    2584              :     {
    2585              :       /* CWE-476: NULL Pointer Dereference.  */
    2586            4 :       ctxt.add_cwe (476);
    2587            4 :       if (ctxt.warn ("calling %qD for first time with NULL as argument 1"
    2588              :                      " has undefined behavior",
    2589              :                      get_callee_fndecl ()))
    2590              :         {
    2591            4 :           inform (ctxt.get_location (),
    2592              :                   "some implementations of %qD may crash on such input",
    2593              :                   get_callee_fndecl ());
    2594            4 :           return true;
    2595              :         }
    2596              :       return false;
    2597              :     }
    2598              : 
    2599              :     bool
    2600            8 :     describe_final_event (pretty_printer &pp,
    2601              :                           const evdesc::final_event &) final override
    2602              :     {
    2603            8 :       pp_printf (&pp,
    2604              :                  "calling %qD for first time with NULL as argument 1"
    2605              :                  " has undefined behavior",
    2606              :                  get_callee_fndecl ());
    2607            8 :       return true;
    2608              :     }
    2609              :   };
    2610              : 
    2611              :   /* An outcome of a "strtok" call.
    2612              :      We have a four-way bifurcation of the analysis via the
    2613              :      4 combinations of two flags:
    2614              :      - m_nonnull_str covers whether the "str" param was null or non-null
    2615              :      - m_found covers whether the result is null or non-null
    2616              :    */
    2617              :   class strtok_call_info : public call_info
    2618              :   {
    2619              :   public:
    2620          512 :     strtok_call_info (const call_details &cd,
    2621              :                       const private_region &private_reg,
    2622              :                       bool nonnull_str,
    2623              :                       bool found)
    2624          512 :     : call_info (cd),
    2625          512 :       m_private_reg (private_reg),
    2626          512 :       m_nonnull_str (nonnull_str),
    2627          512 :       m_found (found)
    2628              :     {
    2629              :     }
    2630              : 
    2631            0 :     void print_desc (pretty_printer &pp) const final override
    2632              :     {
    2633            0 :       if (m_nonnull_str)
    2634              :         {
    2635            0 :           if (m_found)
    2636            0 :             pp_printf (&pp,
    2637              :                        "when %qE on non-NULL string returns non-NULL",
    2638              :                        get_fndecl ());
    2639              :           else
    2640            0 :             pp_printf (&pp,
    2641              :                        "when %qE on non-NULL string returns NULL",
    2642              :                        get_fndecl ());
    2643              :         }
    2644              :       else
    2645              :         {
    2646            0 :           if (m_found)
    2647            0 :             pp_printf (&pp,
    2648              :                        "when %qE with NULL string (using prior) returns"
    2649              :                        " non-NULL",
    2650              :                        get_fndecl ());
    2651              :           else
    2652            0 :             pp_printf (&pp,
    2653              :                        "when %qE with NULL string (using prior) returns NULL",
    2654              :                        get_fndecl ());
    2655              :         }
    2656            0 :     }
    2657              : 
    2658          528 :     bool update_model (region_model *model,
    2659              :                        const exploded_edge *,
    2660              :                        region_model_context *ctxt) const final override
    2661              :     {
    2662          528 :       region_model_manager *mgr = model->get_manager ();
    2663          528 :       const call_details cd (get_call_details (model, ctxt));
    2664          528 :       const svalue *str_sval = cd.get_arg_svalue (0);
    2665              :       /* const svalue *delim_sval = cd.get_arg_svalue (1); */
    2666              : 
    2667          528 :       cd.check_for_null_terminated_string_arg (1);
    2668              :       /* We check that either arg 0 or the private region is null
    2669              :          terminated below.  */
    2670              : 
    2671          528 :       const svalue *null_ptr_sval
    2672          528 :         = mgr->get_or_create_null_ptr (cd.get_arg_type (0));;
    2673          800 :       if (!model->add_constraint (str_sval,
    2674          528 :                                   m_nonnull_str ? NE_EXPR : EQ_EXPR,
    2675              :                                   null_ptr_sval,
    2676              :                                   cd.get_ctxt ()))
    2677              :         return false;
    2678              : 
    2679          312 :       if (m_nonnull_str)
    2680              :         {
    2681              :           /* Update internal buffer.  */
    2682           72 :           model->set_value (&m_private_reg,
    2683              :                             mgr->get_or_create_unmergeable (str_sval),
    2684              :                             ctxt);
    2685              :         }
    2686              :       else
    2687              :         {
    2688              :           /* Read from internal buffer.  */
    2689          240 :           str_sval = model->get_store_value (&m_private_reg, ctxt);
    2690              : 
    2691              :           /* The initial value of the private region is NULL when we're
    2692              :              on a path from main.  */
    2693          480 :           if (const initial_svalue *initial_sval
    2694          240 :                 = str_sval->dyn_cast_initial_svalue ())
    2695           56 :             if (initial_sval->get_region () == &m_private_reg
    2696           56 :                 && model->called_from_main_p ())
    2697              :               {
    2698              :                 /* Implementations of strtok do not necessarily check for NULL
    2699              :                    here, and may crash; see PR analyzer/107573.
    2700              :                    Warn for this, if we were definitely passed NULL.  */
    2701           16 :                 if (cd.get_arg_svalue (0)->all_zeroes_p ())
    2702              :                   {
    2703            8 :                     if (ctxt)
    2704            8 :                       ctxt->warn (::std::make_unique<undefined_behavior> (cd));
    2705              :                   }
    2706              : 
    2707              :                 /* Assume that "str" was actually non-null; terminate
    2708              :                    this path.  */
    2709           16 :                 return false;
    2710              :               }
    2711              : 
    2712              :           /* Now assume str_sval is non-null.  */
    2713          224 :           if (!model->add_constraint (str_sval,
    2714              :                                       NE_EXPR,
    2715              :                                       null_ptr_sval,
    2716              :                                       cd.get_ctxt ()))
    2717              :             return false;
    2718              :         }
    2719              : 
    2720          296 :       const region *buf_reg = model->deref_rvalue (str_sval, NULL_TREE, ctxt);
    2721          296 :       model->scan_for_null_terminator (buf_reg,
    2722              :                                        NULL_TREE,
    2723              :                                        nullptr,
    2724              :                                        ctxt);
    2725              : 
    2726          296 :       if (m_found)
    2727              :         {
    2728          148 :           const region *str_reg
    2729          148 :             = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
    2730              :                                    cd.get_ctxt ());
    2731              :           /* We want to figure out the start and nul terminator
    2732              :              for the token.
    2733              :              For each, we want str_sval + OFFSET for some unknown OFFSET.
    2734              :              Use a conjured_svalue to represent the offset,
    2735              :              using the str_reg as the id of the conjured_svalue.  */
    2736          148 :           const svalue *start_offset
    2737          148 :             = mgr->get_or_create_conjured_svalue (size_type_node,
    2738          148 :                                                   &cd.get_call_stmt (),
    2739              :                                                   str_reg,
    2740          148 :                                                   conjured_purge (model,
    2741          148 :                                                                   ctxt),
    2742              :                                                   0);
    2743          148 :           const svalue *nul_offset
    2744          148 :             = mgr->get_or_create_conjured_svalue (size_type_node,
    2745          148 :                                                   &cd.get_call_stmt (),
    2746              :                                                   str_reg,
    2747          148 :                                                   conjured_purge (model,
    2748          148 :                                                                   ctxt),
    2749              :                                                   1);
    2750              : 
    2751          148 :           tree char_ptr_type = build_pointer_type (char_type_node);
    2752          148 :           const svalue *result
    2753          148 :             = mgr->get_or_create_binop (char_ptr_type, POINTER_PLUS_EXPR,
    2754              :                                         str_sval, start_offset);
    2755          148 :           cd.maybe_set_lhs (result);
    2756              : 
    2757              :           /* nul_offset + 1; the offset to use for the next call.  */
    2758          148 :           const svalue *next_offset
    2759          148 :             = mgr->get_or_create_binop (size_type_node, PLUS_EXPR,
    2760              :                                         nul_offset,
    2761              :                                         mgr->get_or_create_int_cst
    2762          148 :                                         (char_type_node, 1));
    2763              : 
    2764              :           /* Write '\0' to str_sval[nul_offset].  */
    2765          148 :           const svalue *ptr_to_term
    2766          148 :             = mgr->get_or_create_binop (char_ptr_type, POINTER_PLUS_EXPR,
    2767              :                                         str_sval, nul_offset);
    2768          148 :           const region *terminator_reg
    2769          148 :             = model->deref_rvalue (ptr_to_term, NULL_TREE, cd.get_ctxt ());
    2770          148 :           model->set_value (terminator_reg,
    2771              :                             mgr->get_or_create_unmergeable
    2772              :                             (mgr->get_or_create_int_cst (char_type_node,
    2773          148 :                                                          0)),
    2774              :                             cd.get_ctxt ());
    2775              : 
    2776              :           /* Update saved ptr to be at [nul_offset + 1].  */
    2777          148 :           const svalue *ptr_to_next
    2778          148 :             = mgr->get_or_create_binop (cd.get_lhs_type (), POINTER_PLUS_EXPR,
    2779              :                                         str_sval, next_offset);
    2780          148 :           model->set_value (&m_private_reg, ptr_to_next, ctxt);
    2781              :         }
    2782              :       else
    2783          148 :         if (tree lhs_type = cd.get_lhs_type ())
    2784              :           {
    2785          140 :             const svalue *result
    2786          140 :               = mgr->get_or_create_int_cst (lhs_type, 0);
    2787          140 :             cd.maybe_set_lhs (result);
    2788              :           }
    2789              :       return true;
    2790              :     }
    2791              :   private:
    2792              :     const private_region &m_private_reg;
    2793              :     bool m_nonnull_str;
    2794              :     bool m_found;
    2795              :   }; // class strtok_call_info
    2796              : 
    2797         6846 :   kf_strtok (region_model_manager &mgr)
    2798         6846 :   : m_private_reg (mgr.alloc_symbol_id (),
    2799         6846 :                    mgr.get_root_region (),
    2800              :                    get_region_type (),
    2801         6846 :                    "strtok buffer")
    2802              :   {
    2803         6846 :   }
    2804              : 
    2805          896 :   bool matches_call_types_p (const call_details &cd) const final override
    2806              :   {
    2807          896 :     return (cd.num_args () == 2
    2808          896 :             && POINTER_TYPE_P (cd.get_arg_type (0))
    2809         1792 :             && POINTER_TYPE_P (cd.get_arg_type (1)));
    2810              :   }
    2811              : 
    2812          128 :   void impl_call_post (const call_details &cd) const final override
    2813              :   {
    2814          128 :     if (cd.get_ctxt ())
    2815              :       {
    2816              :         /* Four-way bifurcation, based on whether:
    2817              :            - the str is non-null
    2818              :            - the result is non-null
    2819              :            Typically the str is either null or non-null at a particular site,
    2820              :            so hopefully this will generally just lead to two out-edges.  */
    2821          128 :         cd.get_ctxt ()->bifurcate
    2822          128 :           (std::make_unique<strtok_call_info> (cd, m_private_reg, false, false));
    2823          128 :         cd.get_ctxt ()->bifurcate
    2824          128 :           (std::make_unique<strtok_call_info> (cd, m_private_reg, false, true));
    2825          128 :         cd.get_ctxt ()->bifurcate
    2826          128 :           (std::make_unique<strtok_call_info> (cd, m_private_reg, true, false));
    2827          128 :         cd.get_ctxt ()->bifurcate
    2828          128 :           (std::make_unique<strtok_call_info> (cd, m_private_reg, true, true));
    2829          128 :         cd.get_ctxt ()->terminate_path ();
    2830              :       }
    2831          128 :   }
    2832              : 
    2833              : private:
    2834         6846 :   static tree get_region_type ()
    2835              :   {
    2836         6846 :     return build_pointer_type (char_type_node);
    2837              :   }
    2838              :   const private_region m_private_reg;
    2839              : };
    2840              : 
    2841              : /* Handle calls to functions referenced by
    2842              :    __attribute__((malloc(FOO))).  */
    2843              : 
    2844              : void
    2845          487 : region_model::impl_deallocation_call (const call_details &cd)
    2846              : {
    2847          487 :   kf_free kf;
    2848          487 :   kf.impl_call_post (cd);
    2849          487 : }
    2850              : 
    2851              : /* Handler for "strcasecmp"   */
    2852              : 
    2853        10269 : class kf_strcasecmp : public builtin_known_function
    2854              : {
    2855              : public:
    2856              :   bool
    2857            0 :   matches_call_types_p (const call_details &cd) const final override
    2858              :   {
    2859            0 :     return (cd.num_args () == 2
    2860            0 :       && cd.arg_is_pointer_p (0)
    2861            0 :       && cd.arg_is_pointer_p (1));
    2862              :   }
    2863              :   void
    2864            7 :   impl_call_pre (const call_details &cd) const final override
    2865              :   {
    2866            7 :     cd.check_for_null_terminated_string_arg (0);
    2867            7 :     cd.check_for_null_terminated_string_arg (1);
    2868            7 :   }
    2869              : 
    2870              :   enum built_in_function
    2871           56 :   builtin_code () const final override
    2872              :   {
    2873           56 :     return BUILT_IN_STRCASECMP;
    2874              :   }
    2875              : 
    2876              :   void impl_call_post (const call_details &cd) const final override;
    2877              : };
    2878              : 
    2879              : void
    2880            7 : kf_strcasecmp::impl_call_post (const call_details &cd) const
    2881              : {
    2882            7 :   if (cd.get_lhs_type ())
    2883              :     {
    2884            2 :       const svalue *result_val
    2885            2 :         = cd.get_or_create_conjured_svalue (cd.get_lhs_region ());
    2886            2 :       cd.maybe_set_lhs (result_val);
    2887              :     }
    2888            7 : }
    2889              : 
    2890              : static void
    2891         3423 : register_atomic_builtins (known_function_manager &kfm)
    2892              : {
    2893         3423 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE, std::make_unique<kf_atomic_exchange> ());
    2894         3423 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_N, std::make_unique<kf_atomic_exchange_n> ());
    2895         3423 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_1, std::make_unique<kf_atomic_exchange_n> ());
    2896         3423 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_2, std::make_unique<kf_atomic_exchange_n> ());
    2897         3423 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_4, std::make_unique<kf_atomic_exchange_n> ());
    2898         3423 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_8, std::make_unique<kf_atomic_exchange_n> ());
    2899         3423 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_16, std::make_unique<kf_atomic_exchange_n> ());
    2900         3423 :   kfm.add (BUILT_IN_ATOMIC_LOAD, std::make_unique<kf_atomic_load> ());
    2901         3423 :   kfm.add (BUILT_IN_ATOMIC_LOAD_N, std::make_unique<kf_atomic_load_n> ());
    2902         3423 :   kfm.add (BUILT_IN_ATOMIC_LOAD_1, std::make_unique<kf_atomic_load_n> ());
    2903         3423 :   kfm.add (BUILT_IN_ATOMIC_LOAD_2, std::make_unique<kf_atomic_load_n> ());
    2904         3423 :   kfm.add (BUILT_IN_ATOMIC_LOAD_4, std::make_unique<kf_atomic_load_n> ());
    2905         3423 :   kfm.add (BUILT_IN_ATOMIC_LOAD_8, std::make_unique<kf_atomic_load_n> ());
    2906         3423 :   kfm.add (BUILT_IN_ATOMIC_LOAD_16, std::make_unique<kf_atomic_load_n> ());
    2907         3423 :   kfm.add (BUILT_IN_ATOMIC_STORE, std::make_unique<kf_atomic_store> ());
    2908         3423 :   kfm.add (BUILT_IN_ATOMIC_STORE_N, std::make_unique<kf_atomic_store_n> ());
    2909         3423 :   kfm.add (BUILT_IN_ATOMIC_STORE_1, std::make_unique<kf_atomic_store_n> ());
    2910         3423 :   kfm.add (BUILT_IN_ATOMIC_STORE_2, std::make_unique<kf_atomic_store_n> ());
    2911         3423 :   kfm.add (BUILT_IN_ATOMIC_STORE_4, std::make_unique<kf_atomic_store_n> ());
    2912         3423 :   kfm.add (BUILT_IN_ATOMIC_STORE_8, std::make_unique<kf_atomic_store_n> ());
    2913         3423 :   kfm.add (BUILT_IN_ATOMIC_STORE_16, std::make_unique<kf_atomic_store_n> ());
    2914         3423 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_1,
    2915         3423 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2916         3423 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_2,
    2917         3423 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2918         3423 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_4,
    2919         3423 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2920         3423 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_8,
    2921         3423 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2922         3423 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_16,
    2923         3423 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2924         3423 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_1,
    2925         3423 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2926         3423 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_2,
    2927         3423 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2928         3423 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_4,
    2929         3423 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2930         3423 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_8,
    2931         3423 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2932         3423 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_16,
    2933         3423 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2934         3423 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_1,
    2935         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2936         3423 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_2,
    2937         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2938         3423 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_4,
    2939         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2940         3423 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_8,
    2941         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2942         3423 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_16,
    2943         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2944         3423 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_1,
    2945         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2946         3423 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_2,
    2947         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2948         3423 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_4,
    2949         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2950         3423 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_8,
    2951         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2952         3423 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_16,
    2953         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2954         3423 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_1,
    2955         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2956         3423 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_2,
    2957         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2958         3423 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_4,
    2959         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2960         3423 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_8,
    2961         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2962         3423 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_16,
    2963         3423 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2964         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_1,
    2965         3423 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2966         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_2,
    2967         3423 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2968         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_4,
    2969         3423 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2970         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_8,
    2971         3423 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2972         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_16,
    2973         3423 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2974         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_1,
    2975         3423 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2976         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_2,
    2977         3423 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2978         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_4,
    2979         3423 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2980         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_8,
    2981         3423 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2982         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_16,
    2983         3423 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2984         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_1,
    2985         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2986         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_2,
    2987         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2988         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_4,
    2989         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2990         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_8,
    2991         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2992         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_16,
    2993         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2994         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_1,
    2995         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    2996         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_2,
    2997         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    2998         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_4,
    2999         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    3000         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_8,
    3001         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    3002         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_16,
    3003         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    3004         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_1,
    3005         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    3006         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_2,
    3007         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    3008         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_4,
    3009         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    3010         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_8,
    3011         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    3012         3423 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_16,
    3013         3423 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    3014         3423 : }
    3015              : 
    3016              : /* Handle calls to the various IFN_UBSAN_* with no return value.
    3017              :    For now, treat these as no-ops.  */
    3018              : 
    3019        10269 : class kf_ubsan_noop : public internal_known_function
    3020              : {
    3021              : };
    3022              : 
    3023              : /* Handle calls to the various __builtin___ubsan_handle_*.
    3024              :    These can return, but continuing after such a return
    3025              :    isn't likely to be interesting to the user of the analyzer.
    3026              :    Hence we terminate the analysis path at one of these calls.  */
    3027              : 
    3028         3423 : class kf_ubsan_handler : public internal_known_function
    3029              : {
    3030           12 :   void impl_call_post (const call_details &cd) const final override
    3031              :   {
    3032           12 :     if (cd.get_ctxt ())
    3033           12 :       cd.get_ctxt ()->terminate_path ();
    3034           12 :   }
    3035              : };
    3036              : 
    3037              : static void
    3038         3423 : register_sanitizer_builtins (known_function_manager &kfm)
    3039              : {
    3040              :   /* Handle calls to the various IFN_UBSAN_* with no return value.
    3041              :      For now, treat these as no-ops.  */
    3042         3423 :   kfm.add (IFN_UBSAN_NULL,
    3043         3423 :            std::make_unique<kf_ubsan_noop> ());
    3044         3423 :   kfm.add (IFN_UBSAN_BOUNDS,
    3045         3423 :            std::make_unique<kf_ubsan_noop> ());
    3046         3423 :   kfm.add (IFN_UBSAN_PTR,
    3047         3423 :            std::make_unique<kf_ubsan_noop> ());
    3048              : 
    3049         3423 :   kfm.add (BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
    3050         3423 :            std::make_unique<kf_ubsan_handler> ());
    3051         3423 : }
    3052              : 
    3053              : /* Populate KFM with instances of known functions supported by the core of the
    3054              :    analyzer (as opposed to plugins).  */
    3055              : 
    3056              : void
    3057         3423 : register_known_functions (known_function_manager &kfm,
    3058              :                           region_model_manager &rmm)
    3059              : {
    3060              :   /* Debugging/test support functions, all  with a "__analyzer_" prefix.  */
    3061         3423 :   register_known_analyzer_functions (kfm);
    3062              : 
    3063              :   /* Internal fns the analyzer has known_functions for.  */
    3064         3423 :   {
    3065         3423 :     kfm.add (IFN_BUILTIN_EXPECT, std::make_unique<kf_expect> ());
    3066              :   }
    3067              : 
    3068              :   /* GCC built-ins that do not correspond to a function
    3069              :      in the standard library.  */
    3070         3423 :   {
    3071         3423 :     kfm.add (BUILT_IN_EXPECT, std::make_unique<kf_expect> ());
    3072         3423 :     kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, std::make_unique<kf_expect> ());
    3073         3423 :     kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, std::make_unique<kf_alloca> ());
    3074         3423 :     kfm.add (BUILT_IN_STACK_RESTORE, std::make_unique<kf_stack_restore> ());
    3075         3423 :     kfm.add (BUILT_IN_STACK_SAVE, std::make_unique<kf_stack_save> ());
    3076              : 
    3077         3423 :     kfm.add (BUILT_IN_EH_POINTER, std::make_unique<kf_eh_pointer> ());
    3078              : 
    3079         3423 :     kfm.add(BUILT_IN_STRCASECMP, std::make_unique<kf_strcasecmp>());
    3080              : 
    3081         3423 :     register_atomic_builtins (kfm);
    3082         3423 :     register_sanitizer_builtins (kfm);
    3083         3423 :     register_varargs_builtins (kfm);
    3084              :   }
    3085              : 
    3086              :   /* Known builtins and C standard library functions
    3087              :      the analyzer has known functions for.  */
    3088         3423 :   {
    3089         3423 :     kfm.add ("atoi", std::make_unique<kf_atoi_family> ());
    3090         3423 :     kfm.add ("atol", std::make_unique<kf_atoi_family> ());
    3091         3423 :     kfm.add ("atoll", std::make_unique<kf_atoi_family> ());
    3092              : 
    3093         3423 :     kfm.add ("alloca", std::make_unique<kf_alloca> ());
    3094         3423 :     kfm.add ("__builtin_alloca", std::make_unique<kf_alloca> ());
    3095         3423 :     kfm.add ("calloc", std::make_unique<kf_calloc> ());
    3096         3423 :     kfm.add ("__builtin_calloc", std::make_unique<kf_calloc> ());
    3097         3423 :     kfm.add ("free", std::make_unique<kf_free> ());
    3098         3423 :     kfm.add ("__builtin_free", std::make_unique<kf_free> ());
    3099         3423 :     kfm.add ("malloc", std::make_unique<kf_malloc> ());
    3100         3423 :     kfm.add ("__builtin_malloc", std::make_unique<kf_malloc> ());
    3101         3423 :     kfm.add ("memcpy",
    3102         3423 :               std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
    3103         3423 :     kfm.add ("__builtin_memcpy",
    3104         3423 :               std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
    3105         3423 :     kfm.add ("__memcpy_chk", std::make_unique<kf_memcpy_memmove>
    3106         3423 :                               (kf_memcpy_memmove::KF_MEMCPY_CHK));
    3107         3423 :     kfm.add ("__builtin___memcpy_chk", std::make_unique<kf_memcpy_memmove>
    3108         3423 :                               (kf_memcpy_memmove::KF_MEMCPY_CHK));
    3109         3423 :     kfm.add ("memmove",
    3110         3423 :               std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
    3111         3423 :     kfm.add ("__builtin_memmove",
    3112         3423 :               std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
    3113         3423 :     kfm.add ("__memmove_chk", std::make_unique<kf_memcpy_memmove>
    3114         3423 :                               (kf_memcpy_memmove::KF_MEMMOVE_CHK));
    3115         3423 :     kfm.add ("__builtin___memmove_chk", std::make_unique<kf_memcpy_memmove>
    3116         3423 :                               (kf_memcpy_memmove::KF_MEMMOVE_CHK));
    3117         3423 :     kfm.add ("memset", std::make_unique<kf_memset> (false));
    3118         3423 :     kfm.add ("__builtin_memset", std::make_unique<kf_memset> (false));
    3119         3423 :     kfm.add ("__memset_chk", std::make_unique<kf_memset> (true));
    3120         3423 :     kfm.add ("__builtin___memset_chk", std::make_unique<kf_memset> (true));
    3121         3423 :     kfm.add ("realloc", std::make_unique<kf_realloc> ());
    3122         3423 :     kfm.add ("__builtin_realloc", std::make_unique<kf_realloc> ());
    3123         3423 :     kfm.add ("sprintf", std::make_unique<kf_sprintf> ());
    3124         3423 :     kfm.add ("__builtin_sprintf", std::make_unique<kf_sprintf> ());
    3125         3423 :     kfm.add ("strchr", std::make_unique<kf_strchr> ());
    3126         3423 :     kfm.add ("__builtin_strchr", std::make_unique<kf_strchr> ());
    3127         3423 :     kfm.add ("strcpy", std::make_unique<kf_strcpy> (2, false));
    3128         3423 :     kfm.add ("__builtin_strcpy", std::make_unique<kf_strcpy> (2, false));
    3129         3423 :     kfm.add ("__strcpy_chk", std::make_unique<kf_strcpy> (3, true));
    3130         3423 :     kfm.add ("__builtin___strcpy_chk", std::make_unique<kf_strcpy> (3, true));
    3131         3423 :     kfm.add ("strcat", std::make_unique<kf_strcat> (2, false));
    3132         3423 :     kfm.add ("__builtin_strcat", std::make_unique<kf_strcat> (2, false));
    3133         3423 :     kfm.add ("__strcat_chk", std::make_unique<kf_strcat> (3, true));
    3134         3423 :     kfm.add ("__builtin___strcat_chk", std::make_unique<kf_strcat> (3, true));
    3135         3423 :     kfm.add ("strdup", std::make_unique<kf_strdup> ());
    3136         3423 :     kfm.add ("__builtin_strdup", std::make_unique<kf_strdup> ());
    3137         3423 :     kfm.add ("strncpy", std::make_unique<kf_strncpy> ());
    3138         3423 :     kfm.add ("__builtin_strncpy", std::make_unique<kf_strncpy> ());
    3139         3423 :     kfm.add ("strndup", std::make_unique<kf_strndup> ());
    3140         3423 :     kfm.add ("__builtin_strndup", std::make_unique<kf_strndup> ());
    3141         3423 :     kfm.add ("strlen", std::make_unique<kf_strlen> ());
    3142         3423 :     kfm.add ("__builtin_strlen", std::make_unique<kf_strlen> ());
    3143         3423 :     kfm.add ("strstr", std::make_unique<kf_strstr> ());
    3144         3423 :     kfm.add ("__builtin_strstr", std::make_unique<kf_strstr> ());
    3145         3423 :     kfm.add("strcasecmp", std::make_unique<kf_strcasecmp>());
    3146         3423 :     kfm.add("__builtin_strcasecmp", std::make_unique<kf_strcasecmp>());
    3147              :   }
    3148              : 
    3149              :   /* Known POSIX functions, and some non-standard extensions.  */
    3150         3423 :   {
    3151         3423 :     kfm.add ("fopen", std::make_unique<kf_fopen> ());
    3152         3423 :     kfm.add ("getenv", std::make_unique<kf_getenv> ());
    3153         3423 :     kfm.add ("mkdtemp", std::make_unique<kf_mktemp_simple> (
    3154         3423 :                           kf_mktemp_family::outcome::null_ptr));
    3155         3423 :     kfm.add ("mkostemp", std::make_unique<kf_mkostemp> ());
    3156         3423 :     kfm.add ("mkostemps", std::make_unique<kf_mkostemps> ());
    3157         3423 :     kfm.add ("mkstemps", std::make_unique<kf_mkstemps> ());
    3158         3423 :     kfm.add ("mkstemp", std::make_unique<kf_mktemp_simple> (
    3159         3423 :                           kf_mktemp_family::outcome::fd));
    3160              :     /* TODO: Report mktemp as deprecated per MSC24-C
    3161              :        (https://wiki.sei.cmu.edu/confluence/x/hNYxBQ).  */
    3162         3423 :     kfm.add ("mktemp", std::make_unique<kf_mktemp_simple> (
    3163         3423 :                          kf_mktemp_family::outcome::modif_tmpl));
    3164         3423 :     kfm.add ("putenv", std::make_unique<kf_putenv> ());
    3165         3423 :     kfm.add ("strtok", std::make_unique<kf_strtok> (rmm));
    3166              : 
    3167         3423 :     register_known_fd_functions (kfm);
    3168         3423 :     register_known_file_functions (kfm);
    3169              :   }
    3170              : 
    3171              :   /* glibc functions.  */
    3172         3423 :   {
    3173         3423 :     kfm.add ("__errno_location", std::make_unique<kf_errno_location> ());
    3174         3423 :     kfm.add ("error", std::make_unique<kf_error> (3));
    3175         3423 :     kfm.add ("error_at_line", std::make_unique<kf_error> (5));
    3176              :     /* Variants of "error" and "error_at_line" seen by the
    3177              :        analyzer at -O0 (PR analyzer/115724).  */
    3178         3423 :     kfm.add ("__error_alias", std::make_unique<kf_error> (3));
    3179         3423 :     kfm.add ("__error_at_line_alias", std::make_unique<kf_error> (5));
    3180              :   }
    3181              : 
    3182              :   /* Other implementations of C standard library.  */
    3183         3423 :   {
    3184              :     /* According to PR 107807 comment #2, Solaris implements "errno"
    3185              :        like this:
    3186              :          extern int *___errno(void) __attribute__((__const__));
    3187              :          #define errno (*(___errno()))
    3188              :        and macOS like this:
    3189              :          extern int * __error(void);
    3190              :          #define errno (*__error())
    3191              :        and similarly __errno for newlib.
    3192              :        Add these as synonyms for "__errno_location".  */
    3193         3423 :     kfm.add ("___errno", std::make_unique<kf_errno_location> ());
    3194         3423 :     kfm.add ("__error", std::make_unique<kf_errno_location> ());
    3195         3423 :     kfm.add ("__errno", std::make_unique<kf_errno_location> ());
    3196         3423 :     kfm.add ("__get_errno_ptr", std::make_unique<kf_errno_location> ());
    3197              :   }
    3198              : 
    3199              :   /* Language-specific support functions.  */
    3200         3423 :   register_known_functions_lang_cp (kfm);
    3201              : 
    3202              :   /* Some C++ implementations use the std:: copies of these functions
    3203              :      from <cstdlib> etc for the C spellings of these headers (e.g. <stdlib.h>),
    3204              :      so we must match against these too.  */
    3205         3423 :   {
    3206         3423 :     kfm.add_std_ns ("atoi", std::make_unique<kf_atoi_family> ());
    3207         3423 :     kfm.add_std_ns ("atol", std::make_unique<kf_atoi_family> ());
    3208         3423 :     kfm.add_std_ns ("atoll", std::make_unique<kf_atoi_family> ());
    3209              : 
    3210         3423 :     kfm.add_std_ns ("malloc", std::make_unique<kf_malloc> ());
    3211         3423 :     kfm.add_std_ns ("free", std::make_unique<kf_free> ());
    3212         3423 :     kfm.add_std_ns ("realloc", std::make_unique<kf_realloc> ());
    3213         3423 :     kfm.add_std_ns ("calloc", std::make_unique<kf_calloc> ());
    3214         3423 :     kfm.add_std_ns
    3215         3423 :       ("memcpy",
    3216         3423 :        std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
    3217         3423 :     kfm.add_std_ns
    3218         3423 :       ("memmove",
    3219         3423 :        std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
    3220         3423 :     kfm.add_std_ns ("memset", std::make_unique<kf_memset> (false));
    3221         3423 :     kfm.add_std_ns ("strcat", std::make_unique<kf_strcat> (2, false));
    3222         3423 :     kfm.add_std_ns ("strcpy", std::make_unique<kf_strcpy> (2, false));
    3223         3423 :     kfm.add_std_ns ("strlen", std::make_unique<kf_strlen> ());
    3224         3423 :     kfm.add_std_ns ("strncpy", std::make_unique<kf_strncpy> ());
    3225         3423 :     kfm.add_std_ns ("strtok", std::make_unique<kf_strtok> (rmm));
    3226              :   }
    3227         3423 : }
    3228              : 
    3229              : } // namespace ana
    3230              : 
    3231              : #endif /* #if ENABLE_ANALYZER */
        

Generated by: LCOV version 2.4-beta

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