LCOV - code coverage report
Current view: top level - gcc/analyzer - kf.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 97.2 % 1082 1052
Test Date: 2026-02-28 14:20:25 Functions: 96.3 % 109 105
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            8 :   undefined_function_behavior (const call_details &cd)
      41            8 :   : m_call_stmt (cd.get_call_stmt ()),
      42            8 :     m_callee_fndecl (cd.get_fndecl_for_call ())
      43              :   {
      44            8 :     gcc_assert (m_callee_fndecl);
      45            8 :   }
      46              : 
      47           36 :   const char *get_kind () const final override
      48              :   {
      49           36 :     return "undefined_behavior";
      50              :   }
      51              : 
      52            8 :   bool operator== (const undefined_function_behavior &other) const
      53              :   {
      54            8 :     return (&m_call_stmt == &other.m_call_stmt
      55            8 :             && m_callee_fndecl == other.m_callee_fndecl);
      56              :   }
      57              : 
      58            8 :   bool terminate_path_p () const final override { return true; }
      59              : 
      60           16 :   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         2188 : pure_known_function_with_default_return::
      71              : impl_call_pre (const call_details &cd) const
      72              : {
      73         2188 :   cd.set_any_lhs_with_defaults ();
      74         2188 : }
      75              : 
      76              : /* Implementations of specific functions.  */
      77              : 
      78              : /* Handler for "alloca".  */
      79              : 
      80        10131 : class kf_alloca : public builtin_known_function
      81              : {
      82              : public:
      83         1214 :   bool matches_call_types_p (const call_details &cd) const final override
      84              :   {
      85         1214 :     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          422 : kf_alloca::impl_call_pre (const call_details &cd) const
      96              : {
      97          422 :   const svalue *size_sval = cd.get_arg_svalue (0);
      98              : 
      99          422 :   region_model *model = cd.get_model ();
     100          422 :   region_model_manager *mgr = cd.get_manager ();
     101              : 
     102          422 :   const region *new_reg
     103          422 :     = model->create_region_for_alloca (size_sval, cd.get_ctxt ());
     104          422 :   const svalue *ptr_sval
     105          422 :     = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     106          422 :   cd.maybe_set_lhs (ptr_sval);
     107          422 : }
     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         3377 : 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        20262 : 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           35 :   void impl_call_pre (const call_details &cd) const final override
     172              :   {
     173           35 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     174           35 :     tree ptr_tree = cd.get_arg_tree (0);
     175           35 :     const svalue *set_sval = cd.get_arg_svalue (1);
     176              :     /* Ignore the memorder param.  */
     177              : 
     178           35 :     region_model *model = cd.get_model ();
     179           35 :     region_model_context *ctxt = cd.get_ctxt ();
     180              : 
     181           35 :     const region *dst_region = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
     182           35 :     const svalue *ret_sval = model->get_store_value (dst_region, ctxt);
     183           35 :     model->set_value (dst_region, set_sval, ctxt);
     184           35 :     cd.maybe_set_lhs (ret_sval);
     185           35 :   }
     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        84425 :   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        84425 :   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         3377 : 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         3377 : 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        20262 : 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        20262 : 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        10131 : class kf_expect : public internal_known_function
     409              : {
     410              : public:
     411           60 :   void impl_call_pre (const call_details &cd) const final override
     412              :   {
     413              :     /* __builtin_expect's return value is its initial argument.  */
     414           60 :     const svalue *sval = cd.get_arg_svalue (0);
     415           60 :     cd.maybe_set_lhs (sval);
     416           60 :   }
     417              : };
     418              : 
     419              : /* Handler for "calloc".  */
     420              : 
     421        10131 : class kf_calloc : public builtin_known_function
     422              : {
     423              : public:
     424         1394 :   bool matches_call_types_p (const call_details &cd) const final override
     425              :   {
     426         1394 :     return (cd.num_args () == 2
     427         1394 :             && cd.arg_is_size_p (0)
     428         2788 :             && 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          363 : kf_calloc::impl_call_pre (const call_details &cd) const
     440              : {
     441          363 :   region_model *model = cd.get_model ();
     442          363 :   region_model_manager *mgr = cd.get_manager ();
     443          363 :   const svalue *nmemb_sval = cd.get_arg_svalue (0);
     444          363 :   const svalue *size_sval = cd.get_arg_svalue (1);
     445              :   /* TODO: check for overflow here?  */
     446          363 :   const svalue *prod_sval
     447          363 :     = mgr->get_or_create_binop (size_type_node, MULT_EXPR,
     448              :                                 nmemb_sval, size_sval);
     449          363 :   const region *new_reg
     450          363 :     = model->get_or_create_region_for_heap_alloc (prod_sval, cd.get_ctxt ());
     451          363 :   const region *sized_reg
     452          363 :     = mgr->get_sized_region (new_reg, NULL_TREE, prod_sval);
     453          363 :   model->zero_fill_region (sized_reg, cd.get_ctxt ());
     454          363 :   if (cd.get_lhs_type ())
     455              :     {
     456          363 :       const svalue *ptr_sval
     457          363 :         = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     458          363 :       cd.maybe_set_lhs (ptr_sval);
     459              :     }
     460          363 : }
     461              : 
     462              : /* Handler for glibc's "__errno_location".  */
     463              : 
     464        16885 : class kf_errno_location : public known_function
     465              : {
     466              : public:
     467         3129 :   bool matches_call_types_p (const call_details &cd) const final override
     468              :   {
     469         3129 :     return cd.num_args () == 0;
     470              :   }
     471              : 
     472          482 :   void impl_call_pre (const call_details &cd) const final override
     473              :   {
     474          482 :     if (cd.get_lhs_region ())
     475              :       {
     476          482 :         region_model_manager *mgr = cd.get_manager ();
     477          482 :         const region *errno_reg = mgr->get_errno_region ();
     478          482 :         const svalue *errno_ptr = mgr->get_ptr_svalue (cd.get_lhs_type (),
     479              :                                                        errno_reg);
     480          482 :         cd.maybe_set_lhs (errno_ptr);
     481              :       }
     482          482 :   }
     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        13508 :   kf_error (unsigned min_args) : m_min_args (min_args) {}
     493              : 
     494          455 :   bool matches_call_types_p (const call_details &cd) const final override
     495              :   {
     496          455 :     return (cd.num_args () >= m_min_args
     497          455 :             && 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           42 : 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           42 :   tree status = cd.get_arg_tree (0);
     513           42 :   region_model_context *ctxt = cd.get_ctxt ();
     514           42 :   region_model *model = cd.get_model ();
     515           42 :   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           42 :   const int fmt_arg_idx = (m_min_args == 3) ? 2 : 4;
     521           42 :   model->check_for_null_terminated_string_arg (cd, fmt_arg_idx);
     522           42 : }
     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         3377 : class kf_fopen : public known_function
     531              : {
     532              : public:
     533         2569 :   bool matches_call_types_p (const call_details &cd) const final override
     534              :   {
     535         2569 :     return (cd.num_args () == 2
     536         2569 :             && cd.arg_is_pointer_p (0)
     537         5138 :             && cd.arg_is_pointer_p (1));
     538              :   }
     539              : 
     540          620 :   void impl_call_pre (const call_details &cd) const final override
     541              :   {
     542          620 :     cd.check_for_null_terminated_string_arg (0);
     543          620 :     cd.check_for_null_terminated_string_arg (1);
     544          620 :     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          620 :   }
     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        10131 : class kf_free : public builtin_known_function
     567              : {
     568              : public:
     569        67261 :   bool matches_call_types_p (const call_details &cd) const final override
     570              :   {
     571        67261 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
     572              :   }
     573        65760 :   enum built_in_function builtin_code () const final override
     574              :   {
     575        65760 :     return BUILT_IN_FREE;
     576              :   }
     577              :   void impl_call_post (const call_details &cd) const final override;
     578              : };
     579              : 
     580              : void
     581        12042 : kf_free::impl_call_post (const call_details &cd) const
     582              : {
     583        12042 :   const svalue *ptr_sval = cd.get_arg_svalue (0);
     584        12042 :   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        10002 :       region_model *model = cd.get_model ();
     589        10002 :       model->unbind_region_and_descendents (freed_reg, poison_kind::freed);
     590        10002 :       model->unset_dynamic_extents (freed_reg);
     591              :     }
     592        12042 : }
     593              : 
     594              : /* Handle the on_call_pre part of "malloc".  */
     595              : 
     596        10131 : class kf_malloc : public builtin_known_function
     597              : {
     598              : public:
     599        43019 :   bool matches_call_types_p (const call_details &cd) const final override
     600              :   {
     601        43019 :     return (cd.num_args () == 1
     602        43019 :             && cd.arg_is_size_p (0));
     603              :   }
     604        36180 :   enum built_in_function builtin_code () const final override
     605              :   {
     606        36180 :     return BUILT_IN_MALLOC;
     607              :   }
     608              :   void impl_call_pre (const call_details &cd) const final override;
     609              : };
     610              : 
     611              : void
     612         9441 : kf_malloc::impl_call_pre (const call_details &cd) const
     613              : {
     614         9441 :   region_model *model = cd.get_model ();
     615         9441 :   region_model_manager *mgr = cd.get_manager ();
     616         9441 :   const svalue *size_sval = cd.get_arg_svalue (0);
     617         9441 :   const region *new_reg
     618         9441 :     = model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
     619         9441 :   if (cd.get_lhs_type ())
     620              :     {
     621         9441 :       const svalue *ptr_sval
     622         9441 :         = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     623         9441 :       cd.maybe_set_lhs (ptr_sval);
     624              :     }
     625         9441 : }
     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        33770 :   kf_memcpy_memmove (enum kf_memcpy_memmove_variant variant)
     641        33770 :     : m_variant (variant) {};
     642         3154 :   bool matches_call_types_p (const call_details &cd) const final override
     643              :   {
     644         3154 :     return (cd.num_args () == 3
     645         3154 :             && cd.arg_is_pointer_p (0)
     646         3154 :             && cd.arg_is_pointer_p (1)
     647         6308 :             && 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          506 : kf_memcpy_memmove::impl_call_pre (const call_details &cd) const
     672              : {
     673          506 :   const svalue *dest_ptr_sval = cd.get_arg_svalue (0);
     674          506 :   const svalue *src_ptr_sval = cd.get_arg_svalue (1);
     675          506 :   const svalue *num_bytes_sval = cd.get_arg_svalue (2);
     676              : 
     677          506 :   region_model *model = cd.get_model ();
     678              : 
     679          506 :   const region *dest_reg
     680          506 :     = model->deref_rvalue (dest_ptr_sval, cd.get_arg_tree (0), cd.get_ctxt ());
     681          506 :   const region *src_reg
     682          506 :     = model->deref_rvalue (src_ptr_sval, cd.get_arg_tree (1), cd.get_ctxt ());
     683              : 
     684          506 :   cd.maybe_set_lhs (dest_ptr_sval);
     685              :   /* Check for overlap.  */
     686          506 :   switch (m_variant)
     687              :     {
     688          485 :     case KF_MEMCPY:
     689          485 :     case KF_MEMCPY_CHK:
     690          485 :       cd.complain_about_overlap (0, 1, num_bytes_sval);
     691          485 :       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          506 :   model->copy_bytes (dest_reg,
     702              :                      src_reg, cd.get_arg_tree (1),
     703              :                      num_bytes_sval,
     704              :                      cd.get_ctxt ());
     705          506 : }
     706              : 
     707              : /* Handler for "memset" and "__builtin_memset".  */
     708              : 
     709              : class kf_memset : public builtin_known_function
     710              : {
     711              : public:
     712        16885 :   kf_memset (bool chk_variant) : m_chk_variant (chk_variant) {}
     713         3461 :   bool matches_call_types_p (const call_details &cd) const final override
     714              :   {
     715         3461 :     return (cd.num_args () == 3 && cd.arg_is_pointer_p (0));
     716              :   }
     717         3400 :   enum built_in_function builtin_code () const final override
     718              :   {
     719         3400 :     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          640 : kf_memset::impl_call_pre (const call_details &cd) const
     728              : {
     729          640 :   const svalue *dest_sval = cd.get_arg_svalue (0);
     730          640 :   const svalue *fill_value_sval = cd.get_arg_svalue (1);
     731          640 :   const svalue *num_bytes_sval = cd.get_arg_svalue (2);
     732              : 
     733          640 :   region_model *model = cd.get_model ();
     734          640 :   region_model_manager *mgr = cd.get_manager ();
     735              : 
     736          640 :   const region *dest_reg
     737          640 :     = model->deref_rvalue (dest_sval, cd.get_arg_tree (0), cd.get_ctxt ());
     738              : 
     739          640 :   const svalue *fill_value_u8
     740          640 :     = mgr->get_or_create_cast (unsigned_char_type_node, fill_value_sval);
     741              : 
     742          640 :   const region *sized_dest_reg = mgr->get_sized_region (dest_reg,
     743              :                                                         NULL_TREE,
     744              :                                                         num_bytes_sval);
     745          640 :   model->fill_region (sized_dest_reg, fill_value_u8, cd.get_ctxt ());
     746              : 
     747          640 :   cd.maybe_set_lhs (dest_sval);
     748          640 : }
     749              : 
     750              : /* A subclass of pending_diagnostic for complaining about 'putenv'
     751              :    called on an auto var.  */
     752              : 
     753              : class putenv_of_auto_var
     754              : : public pending_diagnostic_subclass<putenv_of_auto_var>
     755              : {
     756              : public:
     757            7 :   putenv_of_auto_var (tree fndecl, const region *reg)
     758            7 :   : m_fndecl (fndecl), m_reg (reg),
     759            7 :     m_var_decl (reg->get_base_region ()->maybe_get_decl ())
     760              :   {
     761            7 :   }
     762              : 
     763          106 :   const char *get_kind () const final override
     764              :   {
     765          106 :     return "putenv_of_auto_var";
     766              :   }
     767              : 
     768            7 :   bool operator== (const putenv_of_auto_var &other) const
     769              :   {
     770            7 :     return (m_fndecl == other.m_fndecl
     771            7 :             && m_reg == other.m_reg
     772           14 :             && same_tree_p (m_var_decl, other.m_var_decl));
     773              :   }
     774              : 
     775           14 :   int get_controlling_option () const final override
     776              :   {
     777           14 :     return OPT_Wanalyzer_putenv_of_auto_var;
     778              :   }
     779              : 
     780            7 :   bool emit (diagnostic_emission_context &ctxt) final override
     781              :   {
     782            7 :     auto_diagnostic_group d;
     783              : 
     784              :     /* SEI CERT C Coding Standard: "POS34-C. Do not call putenv() with a
     785              :        pointer to an automatic variable as the argument".  */
     786            7 :     diagnostics::metadata::precanned_rule
     787            7 :       rule ("POS34-C", "https://wiki.sei.cmu.edu/confluence/x/6NYxBQ");
     788            7 :     ctxt.add_rule (rule);
     789              : 
     790            7 :     bool warned;
     791            7 :     if (m_var_decl)
     792            6 :       warned = ctxt.warn ("%qE on a pointer to automatic variable %qE",
     793              :                           m_fndecl, m_var_decl);
     794              :     else
     795            1 :       warned = ctxt.warn ("%qE on a pointer to an on-stack buffer",
     796              :                           m_fndecl);
     797            7 :     if (warned)
     798              :       {
     799            7 :         if (m_var_decl)
     800            6 :           inform (DECL_SOURCE_LOCATION (m_var_decl),
     801              :                   "%qE declared on stack here", m_var_decl);
     802            7 :         inform (ctxt.get_location (), "perhaps use %qs rather than %qE",
     803              :                 "setenv", m_fndecl);
     804              :       }
     805              : 
     806           14 :     return warned;
     807            7 :   }
     808              : 
     809              :   bool
     810           14 :   describe_final_event (pretty_printer &pp,
     811              :                         const evdesc::final_event &) final override
     812              :   {
     813           14 :     if (m_var_decl)
     814           12 :       pp_printf  (&pp,
     815              :                   "%qE on a pointer to automatic variable %qE",
     816              :                   m_fndecl, m_var_decl);
     817              :     else
     818            2 :       pp_printf  (&pp,
     819              :                   "%qE on a pointer to an on-stack buffer",
     820              :                   m_fndecl);
     821           14 :     return true;
     822              :   }
     823              : 
     824            7 :   void mark_interesting_stuff (interesting_t *interest) final override
     825              :   {
     826            7 :     if (!m_var_decl)
     827            1 :       interest->add_region_creation (m_reg->get_base_region ());
     828            7 :   }
     829              : 
     830              : private:
     831              :   tree m_fndecl; // non-NULL
     832              :   const region *m_reg; // non-NULL
     833              :   tree m_var_decl; // could be NULL
     834              : };
     835              : 
     836              : /* Handler for calls to "putenv".
     837              : 
     838              :    In theory we could try to model the state of the environment variables
     839              :    for the process; for now we merely complain about putenv of regions
     840              :    on the stack.  */
     841              : 
     842         3377 : class kf_putenv : public known_function
     843              : {
     844              : public:
     845          118 :   bool matches_call_types_p (const call_details &cd) const final override
     846              :   {
     847          118 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
     848              :   }
     849              : 
     850           17 :   void impl_call_pre (const call_details &cd) const final override
     851              :   {
     852           17 :     tree fndecl = cd.get_fndecl_for_call ();
     853           17 :     gcc_assert (fndecl);
     854           17 :     region_model_context *ctxt = cd.get_ctxt ();
     855           17 :     region_model *model = cd.get_model ();
     856           17 :     model->check_for_null_terminated_string_arg (cd, 0);
     857           17 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     858           17 :     const region *reg
     859           17 :       = model->deref_rvalue (ptr_sval, cd.get_arg_tree (0), ctxt);
     860           17 :     store_manager *store_mgr = model->get_manager ()->get_store_manager ();
     861           17 :     model->get_store ()->mark_as_escaped (*store_mgr, reg->get_base_region ());
     862           17 :     enum memory_space mem_space = reg->get_memory_space ();
     863           17 :     switch (mem_space)
     864              :       {
     865            0 :       default:
     866            0 :         gcc_unreachable ();
     867              :       case MEMSPACE_UNKNOWN:
     868              :       case MEMSPACE_CODE:
     869              :       case MEMSPACE_GLOBALS:
     870              :       case MEMSPACE_HEAP:
     871              :       case MEMSPACE_READONLY_DATA:
     872              :         break;
     873            7 :       case MEMSPACE_STACK:
     874            7 :         if (ctxt)
     875            7 :           ctxt->warn (std::make_unique<putenv_of_auto_var> (fndecl, reg));
     876              :         break;
     877              :       }
     878           17 :     cd.set_any_lhs_with_defaults ();
     879           17 :   }
     880              : };
     881              : 
     882              : /* Handler for "realloc":
     883              : 
     884              :      void *realloc(void *ptr, size_t size);
     885              : 
     886              :    realloc(3) is awkward, since it has various different outcomes
     887              :    that are best modelled as separate exploded nodes/edges.
     888              : 
     889              :    We first check for sm-state, in
     890              :    malloc_state_machine::on_realloc_call, so that we
     891              :    can complain about issues such as realloc of a non-heap
     892              :    pointer, and terminate the path for such cases (and issue
     893              :    the complaints at the call's exploded node).
     894              : 
     895              :    Assuming that these checks pass, we split the path here into
     896              :    three special cases (and terminate the "standard" path):
     897              :    (A) failure, returning NULL
     898              :    (B) success, growing the buffer in-place without moving it
     899              :    (C) success, allocating a new buffer, copying the content
     900              :    of the old buffer to it, and freeing the old buffer.
     901              : 
     902              :    Each of these has a custom_edge_info subclass, which updates
     903              :    the region_model and sm-state of the destination state.  */
     904              : 
     905        10131 : class kf_realloc : public builtin_known_function
     906              : {
     907              : public:
     908         1980 :   bool matches_call_types_p (const call_details &cd) const final override
     909              :   {
     910         1980 :     return (cd.num_args () == 2
     911         1980 :             && cd.arg_is_pointer_p (0)
     912         3960 :             && cd.arg_is_size_p (1));
     913              :   }
     914              : 
     915         2004 :   enum built_in_function builtin_code () const final override
     916              :   {
     917         2004 :     return BUILT_IN_REALLOC;
     918              :   }
     919              : 
     920              :   void impl_call_post (const call_details &cd) const final override;
     921              : };
     922              : 
     923              : void
     924          310 : kf_realloc::impl_call_post (const call_details &cd) const
     925              : {
     926              :   /* Three custom subclasses of custom_edge_info, for handling the various
     927              :      outcomes of "realloc".  */
     928              : 
     929              :   /* Concrete custom_edge_info: a realloc call that fails, returning NULL.  */
     930            0 :   class failure : public failed_call_info
     931              :   {
     932              :   public:
     933          310 :     failure (const call_details &cd)
     934          310 :     : failed_call_info (cd)
     935              :     {
     936              :     }
     937              : 
     938          354 :     bool update_model (region_model *model,
     939              :                        const exploded_edge *,
     940              :                        region_model_context *ctxt) const final override
     941              :     {
     942              :       /* Return NULL; everything else is unchanged.  */
     943          354 :       const call_details cd (get_call_details (model, ctxt));
     944          354 :       region_model_manager *mgr = cd.get_manager ();
     945          354 :       if (cd.get_lhs_type ())
     946              :         {
     947          345 :           const svalue *zero
     948          345 :             = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
     949          345 :           model->set_value (cd.get_lhs_region (),
     950              :                             zero,
     951              :                             cd.get_ctxt ());
     952              :         }
     953          354 :       return true;
     954              :     }
     955              :   };
     956              : 
     957              :   /* Concrete custom_edge_info: a realloc call that succeeds, growing
     958              :      the existing buffer without moving it.  */
     959            0 :   class success_no_move : public call_info
     960              :   {
     961              :   public:
     962          310 :     success_no_move (const call_details &cd)
     963          310 :     : call_info (cd)
     964              :     {
     965              :     }
     966              : 
     967            8 :     void print_desc (pretty_printer &pp) const final override
     968              :     {
     969            8 :       pp_printf (&pp,
     970              :                  "when %qE succeeds, without moving buffer",
     971              :                  get_fndecl ());
     972            8 :     }
     973              : 
     974          339 :     bool update_model (region_model *model,
     975              :                        const exploded_edge *,
     976              :                        region_model_context *ctxt) const final override
     977              :     {
     978              :       /* Update size of buffer and return the ptr unchanged.  */
     979          339 :       const call_details cd (get_call_details (model, ctxt));
     980          339 :       region_model_manager *mgr = cd.get_manager ();
     981          339 :       const svalue *ptr_sval = cd.get_arg_svalue (0);
     982          339 :       const svalue *size_sval = cd.get_arg_svalue (1);
     983              : 
     984              :       /* We can only grow in place with a non-NULL pointer.  */
     985          339 :       {
     986          339 :         const svalue *null_ptr
     987          339 :           = mgr->get_or_create_int_cst (ptr_sval->get_type (), 0);
     988          339 :         if (!model->add_constraint (ptr_sval, NE_EXPR, null_ptr,
     989              :                                     cd.get_ctxt ()))
     990              :           return false;
     991              :       }
     992              : 
     993          333 :       if (const region *buffer_reg = model->deref_rvalue (ptr_sval, NULL_TREE,
     994              :                                                           ctxt))
     995          333 :         if (compat_types_p (size_sval->get_type (), size_type_node))
     996          333 :           model->set_dynamic_extents (buffer_reg, size_sval, ctxt);
     997          333 :       if (cd.get_lhs_region ())
     998              :         {
     999          324 :           model->set_value (cd.get_lhs_region (), ptr_sval, cd.get_ctxt ());
    1000          324 :           const svalue *zero
    1001          324 :             = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
    1002          324 :           return model->add_constraint (ptr_sval, NE_EXPR, zero, ctxt);
    1003              :         }
    1004              :       else
    1005              :         return true;
    1006              :     }
    1007              :   };
    1008              : 
    1009              :   /* Concrete custom_edge_info: a realloc call that succeeds, freeing
    1010              :      the existing buffer and moving the content to a freshly allocated
    1011              :      buffer.  */
    1012            0 :   class success_with_move : public call_info
    1013              :   {
    1014              :   public:
    1015          310 :     success_with_move (const call_details &cd)
    1016          310 :     : call_info (cd)
    1017              :     {
    1018              :     }
    1019              : 
    1020           72 :     void print_desc (pretty_printer &pp) const final override
    1021              :     {
    1022           72 :       pp_printf (&pp,
    1023              :                  "when %qE succeeds, moving buffer",
    1024              :                  get_fndecl ());
    1025           72 :     }
    1026          394 :     bool update_model (region_model *model,
    1027              :                        const exploded_edge *,
    1028              :                        region_model_context *ctxt) const final override
    1029              :     {
    1030          394 :       const call_details cd (get_call_details (model, ctxt));
    1031          394 :       region_model_manager *mgr = cd.get_manager ();
    1032          394 :       const svalue *old_ptr_sval = cd.get_arg_svalue (0);
    1033          394 :       const svalue *new_size_sval = cd.get_arg_svalue (1);
    1034              : 
    1035              :       /* Create the new region.  */
    1036          394 :       const region *new_reg
    1037          394 :         = model->get_or_create_region_for_heap_alloc (new_size_sval, ctxt);
    1038          394 :       const svalue *new_ptr_sval
    1039          394 :         = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
    1040          394 :       if (!model->add_constraint (new_ptr_sval, NE_EXPR, old_ptr_sval,
    1041              :                                   cd.get_ctxt ()))
    1042              :         return false;
    1043              : 
    1044          394 :       if (cd.get_lhs_type ())
    1045          385 :         cd.maybe_set_lhs (new_ptr_sval);
    1046              : 
    1047          394 :       if (const region *freed_reg = model->deref_rvalue (old_ptr_sval,
    1048              :                                                          NULL_TREE, ctxt))
    1049              :         {
    1050              :           /* Copy the data.  */
    1051          394 :           const svalue *old_size_sval = model->get_dynamic_extents (freed_reg);
    1052          394 :           if (old_size_sval)
    1053              :             {
    1054          167 :               const svalue *copied_size_sval
    1055          167 :                 = get_copied_size (model, old_size_sval, new_size_sval);
    1056          167 :               const region *copied_old_reg
    1057          167 :                 = mgr->get_sized_region (freed_reg, nullptr, copied_size_sval);
    1058          167 :               const svalue *buffer_content_sval
    1059          167 :                 = model->get_store_value (copied_old_reg, cd.get_ctxt ());
    1060          167 :               const region *copied_new_reg
    1061          167 :                 = mgr->get_sized_region (new_reg, nullptr, copied_size_sval);
    1062          167 :               model->set_value (copied_new_reg, buffer_content_sval,
    1063              :                                 cd.get_ctxt ());
    1064              :             }
    1065              :           else
    1066              :             {
    1067              :               /* We don't know how big the old region was;
    1068              :                  mark the new region as having been touched to avoid uninit
    1069              :                  issues.  */
    1070          227 :               model->mark_region_as_unknown (new_reg, cd.get_uncertainty ());
    1071              :             }
    1072              : 
    1073              :           /* Free the old region, so that pointers to the old buffer become
    1074              :              invalid.  */
    1075              : 
    1076              :           /* If the ptr points to an underlying heap region, delete it,
    1077              :              poisoning pointers.  */
    1078          394 :           model->unbind_region_and_descendents (freed_reg, poison_kind::freed);
    1079          394 :           model->unset_dynamic_extents (freed_reg);
    1080              :         }
    1081              : 
    1082              :       /* Update the sm-state: mark the old_ptr_sval as "freed",
    1083              :          and the new_ptr_sval as "nonnull".  */
    1084          394 :       model->on_realloc_with_move (cd, old_ptr_sval, new_ptr_sval);
    1085              : 
    1086          394 :       if (cd.get_lhs_type ())
    1087              :         {
    1088          385 :           const svalue *zero
    1089          385 :             = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
    1090          385 :           return model->add_constraint (new_ptr_sval, NE_EXPR, zero,
    1091          385 :                                         cd.get_ctxt ());
    1092              :         }
    1093              :       else
    1094              :         return true;
    1095              :     }
    1096              : 
    1097              :   private:
    1098              :     /* Return the lesser of OLD_SIZE_SVAL and NEW_SIZE_SVAL.
    1099              :        If unknown, OLD_SIZE_SVAL is returned.  */
    1100          167 :     const svalue *get_copied_size (region_model *model,
    1101              :                                    const svalue *old_size_sval,
    1102              :                                    const svalue *new_size_sval) const
    1103              :     {
    1104          167 :       tristate res
    1105          167 :         = model->eval_condition (old_size_sval, GT_EXPR, new_size_sval);
    1106          167 :       switch (res.get_value ())
    1107              :         {
    1108              :         case tristate::TS_TRUE:
    1109              :           return new_size_sval;
    1110          135 :         case tristate::TS_FALSE:
    1111          135 :         case tristate::TS_UNKNOWN:
    1112          135 :           return old_size_sval;
    1113            0 :         default:
    1114            0 :           gcc_unreachable ();
    1115              :         }
    1116              :     }
    1117              :   };
    1118              : 
    1119              :   /* Body of kf_realloc::impl_call_post.  */
    1120              : 
    1121          310 :   if (cd.get_ctxt ())
    1122              :     {
    1123          310 :       cd.get_ctxt ()->bifurcate (std::make_unique<failure> (cd));
    1124          310 :       cd.get_ctxt ()->bifurcate (std::make_unique<success_no_move> (cd));
    1125          310 :       cd.get_ctxt ()->bifurcate (std::make_unique<success_with_move> (cd));
    1126          310 :       cd.get_ctxt ()->terminate_path ();
    1127              :     }
    1128          310 : }
    1129              : 
    1130              : /* Handler for "strchr" and "__builtin_strchr".  */
    1131              : 
    1132         6754 : class kf_strchr : public builtin_known_function
    1133              : {
    1134              : public:
    1135          153 :   bool matches_call_types_p (const call_details &cd) const final override
    1136              :   {
    1137          153 :     return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
    1138              :   }
    1139           22 :   void impl_call_pre (const call_details &cd) const final override
    1140              :   {
    1141           22 :     cd.check_for_null_terminated_string_arg (0);
    1142           22 :   }
    1143              : 
    1144          176 :   enum built_in_function builtin_code () const final override
    1145              :   {
    1146          176 :     return BUILT_IN_STRCHR;
    1147              :   }
    1148              :   void impl_call_post (const call_details &cd) const final override;
    1149              : };
    1150              : 
    1151              : void
    1152           21 : kf_strchr::impl_call_post (const call_details &cd) const
    1153              : {
    1154            0 :   class strchr_call_info : public call_info
    1155              :   {
    1156              :   public:
    1157           42 :     strchr_call_info (const call_details &cd, bool found)
    1158           42 :     : call_info (cd), m_found (found)
    1159              :     {
    1160              :     }
    1161              : 
    1162           38 :     void print_desc (pretty_printer &pp) const final override
    1163              :     {
    1164           38 :       if (m_found)
    1165           18 :         pp_printf (&pp,
    1166              :                    "when %qE returns non-NULL",
    1167              :                    get_fndecl ());
    1168              :       else
    1169           20 :         pp_printf (&pp,
    1170              :                    "when %qE returns NULL",
    1171              :                    get_fndecl ());
    1172           38 :     }
    1173              : 
    1174           65 :     bool update_model (region_model *model,
    1175              :                        const exploded_edge *,
    1176              :                        region_model_context *ctxt) const final override
    1177              :     {
    1178           65 :       const call_details cd (get_call_details (model, ctxt));
    1179           65 :       if (tree lhs_type = cd.get_lhs_type ())
    1180              :         {
    1181           63 :           region_model_manager *mgr = model->get_manager ();
    1182           63 :           const svalue *result;
    1183           63 :           if (m_found)
    1184              :             {
    1185           31 :               const svalue *str_sval = cd.get_arg_svalue (0);
    1186           31 :               const region *str_reg
    1187           31 :                 = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
    1188              :                                        cd.get_ctxt ());
    1189              :               /* We want str_sval + OFFSET for some unknown OFFSET.
    1190              :                  Use a conjured_svalue to represent the offset,
    1191              :                  using the str_reg as the id of the conjured_svalue.  */
    1192           31 :               const svalue *offset
    1193           31 :                 = mgr->get_or_create_conjured_svalue (size_type_node,
    1194           31 :                                                       &cd.get_call_stmt (),
    1195              :                                                       str_reg,
    1196           31 :                                                       conjured_purge (model,
    1197           31 :                                                                       ctxt));
    1198           31 :               result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR,
    1199              :                                                  str_sval, offset);
    1200              :             }
    1201              :           else
    1202           32 :             result = mgr->get_or_create_int_cst (lhs_type, 0);
    1203           63 :           cd.maybe_set_lhs (result);
    1204              :         }
    1205           65 :       return true;
    1206              :     }
    1207              :   private:
    1208              :     bool m_found;
    1209              :   };
    1210              : 
    1211              :   /* Body of kf_strchr::impl_call_post.  */
    1212           21 :   if (cd.get_ctxt ())
    1213              :     {
    1214           21 :       cd.get_ctxt ()->bifurcate (std::make_unique<strchr_call_info> (cd, false));
    1215           21 :       cd.get_ctxt ()->bifurcate (std::make_unique<strchr_call_info> (cd, true));
    1216           21 :       cd.get_ctxt ()->terminate_path ();
    1217              :     }
    1218           21 : }
    1219              : 
    1220              : /* Handler for "sprintf".
    1221              :      int sprintf(char *str, const char *format, ...);
    1222              : */
    1223              : 
    1224         6754 : class kf_sprintf : public builtin_known_function
    1225              : {
    1226              : public:
    1227         2942 :   bool matches_call_types_p (const call_details &cd) const final override
    1228              :   {
    1229         2942 :     return (cd.num_args () >= 2
    1230         2942 :             && cd.arg_is_pointer_p (0)
    1231         5884 :             && cd.arg_is_pointer_p (1));
    1232              :   }
    1233              : 
    1234          632 :   enum built_in_function builtin_code () const final override
    1235              :   {
    1236          632 :     return BUILT_IN_SPRINTF;
    1237              :   }
    1238              : 
    1239         1278 :   void impl_call_pre (const call_details &cd) const final override
    1240              :   {
    1241              :     /* For now, merely assume that the destination buffer gets set to a
    1242              :        new svalue.  */
    1243         1278 :     region_model *model = cd.get_model ();
    1244         1278 :     region_model_context *ctxt = cd.get_ctxt ();
    1245         1278 :     const svalue *dst_ptr = cd.get_arg_svalue (0);
    1246         1278 :     const region *dst_reg
    1247         1278 :       = model->deref_rvalue (dst_ptr, cd.get_arg_tree (0), ctxt);
    1248              :     /* Restrict the region we consider to be affected to the valid capacity
    1249              :        so that we don't trigger buffer overflow false positives.  */
    1250         1278 :     const svalue *capacity = model->get_capacity (dst_reg);
    1251         1278 :     dst_reg = model->get_manager ()->get_sized_region (dst_reg,
    1252              :                                                        NULL_TREE,
    1253              :                                                        capacity);
    1254         1278 :     const svalue *content = cd.get_or_create_conjured_svalue (dst_reg);
    1255         1278 :     model->set_value (dst_reg, content, ctxt);
    1256         1278 :     cd.set_any_lhs_with_defaults ();
    1257         1278 :   }
    1258              : };
    1259              : 
    1260              : /* Handler for "__builtin_stack_restore".  */
    1261              : 
    1262         3377 : class kf_stack_restore : public pure_known_function_with_default_return
    1263              : {
    1264              : public:
    1265            0 :   bool matches_call_types_p (const call_details &) const final override
    1266              :   {
    1267            0 :     return true;
    1268              :   }
    1269              : 
    1270              :   /* Currently a no-op.  */
    1271              : };
    1272              : 
    1273              : /* Handler for "__builtin_stack_save".  */
    1274              : 
    1275         3377 : class kf_stack_save : public pure_known_function_with_default_return
    1276              : {
    1277              : public:
    1278            0 :   bool matches_call_types_p (const call_details &) const final override
    1279              :   {
    1280            0 :     return true;
    1281              :   }
    1282              : 
    1283              :   /* Currently a no-op.  */
    1284              : };
    1285              : 
    1286              : /* Handler for "__builtin_eh_pointer".  */
    1287              : 
    1288         3377 : class kf_eh_pointer : public builtin_known_function
    1289              : {
    1290              : public:
    1291            0 :   bool matches_call_types_p (const call_details &) const final override
    1292              :   {
    1293            0 :     return true;
    1294              :   }
    1295              : 
    1296          992 :   enum built_in_function builtin_code () const final override
    1297              :   {
    1298          992 :     return BUILT_IN_EH_POINTER;
    1299              :   }
    1300              : 
    1301          193 :   void impl_call_pre (const call_details &cd) const final override
    1302              :   {
    1303          193 :     cd.set_any_lhs_with_defaults ();
    1304          193 :   }
    1305              : };
    1306              : 
    1307              : /* Handler for "strcat" and "__builtin_strcat_chk".  */
    1308              : 
    1309              : class kf_strcat : public builtin_known_function
    1310              : {
    1311              : public:
    1312        16885 :   kf_strcat (unsigned int num_args, bool chk_variant)
    1313        16885 :     : m_num_args (num_args),
    1314        16885 :       m_chk_variant (chk_variant) {}
    1315          395 :   bool matches_call_types_p (const call_details &cd) const final override
    1316              :   {
    1317          395 :     return (cd.num_args () == m_num_args
    1318          395 :             && cd.arg_is_pointer_p (0)
    1319          790 :             && cd.arg_is_pointer_p (1));
    1320              :   }
    1321              : 
    1322          456 :   enum built_in_function builtin_code () const final override
    1323              :   {
    1324          456 :     return m_chk_variant ? BUILT_IN_STRCAT_CHK : BUILT_IN_STRCAT;
    1325              :   }
    1326              : 
    1327           57 :   void impl_call_pre (const call_details &cd) const final override
    1328              :   {
    1329           57 :     region_model *model = cd.get_model ();
    1330           57 :     region_model_manager *mgr = cd.get_manager ();
    1331              : 
    1332           57 :     const svalue *dest_sval = cd.get_arg_svalue (0);
    1333           57 :     const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0),
    1334              :                                                   cd.get_ctxt ());
    1335              : 
    1336           57 :     const svalue *dst_strlen_sval
    1337           57 :       = cd.check_for_null_terminated_string_arg (0, false, nullptr);
    1338           57 :     if (!dst_strlen_sval)
    1339              :       {
    1340            2 :         if (cd.get_ctxt ())
    1341            2 :           cd.get_ctxt ()->terminate_path ();
    1342            4 :         return;
    1343              :       }
    1344              : 
    1345           55 :     const svalue *bytes_to_copy;
    1346           55 :     const svalue *num_src_bytes_read_sval
    1347           55 :       = cd.check_for_null_terminated_string_arg (1, true, &bytes_to_copy);
    1348           55 :     if (!num_src_bytes_read_sval)
    1349              :       {
    1350            2 :         if (cd.get_ctxt ())
    1351            2 :           cd.get_ctxt ()->terminate_path ();
    1352            2 :         return;
    1353              :       }
    1354              : 
    1355           53 :     cd.maybe_set_lhs (dest_sval);
    1356           53 :     cd.complain_about_overlap (0, 1, num_src_bytes_read_sval);
    1357              : 
    1358           53 :     const region *offset_reg
    1359           53 :       = mgr->get_offset_region (dest_reg, NULL_TREE, dst_strlen_sval);
    1360           53 :     model->write_bytes (offset_reg,
    1361              :                         num_src_bytes_read_sval,
    1362              :                         bytes_to_copy,
    1363              :                         cd.get_ctxt ());
    1364              :   }
    1365              : 
    1366              : private:
    1367              :   unsigned int m_num_args;
    1368              :   const bool m_chk_variant;
    1369              : };
    1370              : 
    1371              : /* Handler for "strcpy" and "__builtin_strcpy_chk".  */
    1372              : 
    1373              : class kf_strcpy : public builtin_known_function
    1374              : {
    1375              : public:
    1376        16885 :   kf_strcpy (unsigned int num_args, bool chk_variant)
    1377        16885 :     : m_num_args (num_args),
    1378        16885 :       m_chk_variant (chk_variant) {}
    1379         2039 :   bool matches_call_types_p (const call_details &cd) const final override
    1380              :   {
    1381         2039 :     return (cd.num_args () == m_num_args
    1382         2039 :             && cd.arg_is_pointer_p (0)
    1383         4078 :             && cd.arg_is_pointer_p (1));
    1384              :   }
    1385          656 :   enum built_in_function builtin_code () const final override
    1386              :   {
    1387          656 :     return m_chk_variant ? BUILT_IN_STRCPY_CHK : BUILT_IN_STRCPY;
    1388              :   }
    1389              :   void impl_call_pre (const call_details &cd) const final override;
    1390              : 
    1391              : private:
    1392              :   unsigned int m_num_args;
    1393              :   const bool m_chk_variant;
    1394              : };
    1395              : 
    1396              : void
    1397          816 : kf_strcpy::impl_call_pre (const call_details &cd) const
    1398              : {
    1399          816 :   region_model *model = cd.get_model ();
    1400          816 :   region_model_context *ctxt = cd.get_ctxt ();
    1401              : 
    1402          816 :   const svalue *dest_sval = cd.get_arg_svalue (0);
    1403          816 :   const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0),
    1404              :                                                     ctxt);
    1405              :   /* strcpy returns the initial param.  */
    1406          816 :   cd.maybe_set_lhs (dest_sval);
    1407              : 
    1408          816 :   const svalue *bytes_to_copy = nullptr;
    1409         1632 :   if (const svalue *num_bytes_read_sval
    1410          816 :       = cd.check_for_null_terminated_string_arg (1, true, &bytes_to_copy))
    1411              :     {
    1412          813 :       gcc_assert (bytes_to_copy);
    1413          813 :       cd.complain_about_overlap (0, 1, num_bytes_read_sval);
    1414          813 :       model->write_bytes (dest_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
    1415              :     }
    1416              :   else
    1417              :     {
    1418            3 :       if (cd.get_ctxt ())
    1419            3 :         cd.get_ctxt ()->terminate_path ();
    1420              :     }
    1421          816 : }
    1422              : 
    1423              : /* Handler for "strdup" and "__builtin_strdup".  */
    1424              : 
    1425         6754 : class kf_strdup : public builtin_known_function
    1426              : {
    1427              : public:
    1428         1432 :   bool matches_call_types_p (const call_details &cd) const final override
    1429              :   {
    1430         1432 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
    1431              :   }
    1432         1326 :   enum built_in_function builtin_code () const final override
    1433              :   {
    1434         1326 :     return BUILT_IN_STRDUP;
    1435              :   }
    1436          275 :   void impl_call_pre (const call_details &cd) const final override
    1437              :   {
    1438          275 :     region_model *model = cd.get_model ();
    1439          275 :     region_model_context *ctxt = cd.get_ctxt ();
    1440          275 :     region_model_manager *mgr = cd.get_manager ();
    1441          275 :     const svalue *bytes_to_copy;
    1442          550 :     if (const svalue *num_bytes_read_sval
    1443          275 :         = cd.check_for_null_terminated_string_arg (0, true, &bytes_to_copy))
    1444              :       {
    1445          273 :         const region *new_reg
    1446          273 :           = model->get_or_create_region_for_heap_alloc (num_bytes_read_sval,
    1447              :                                                         ctxt);
    1448          273 :         model->write_bytes (new_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
    1449          273 :         if (cd.get_lhs_type ())
    1450              :           {
    1451          273 :             const svalue *ptr_sval
    1452          273 :               = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
    1453          273 :             cd.maybe_set_lhs (ptr_sval);
    1454              :           }
    1455              :       }
    1456              :     else
    1457              :       {
    1458            2 :         if (ctxt)
    1459            2 :           ctxt->terminate_path ();
    1460              :       }
    1461          275 :   }
    1462              : };
    1463              : 
    1464              : /* Handler for "strlen" and for "__analyzer_get_strlen".  */
    1465              : 
    1466        13508 : class kf_strlen : public builtin_known_function
    1467              : {
    1468              : public:
    1469         9934 :   bool matches_call_types_p (const call_details &cd) const final override
    1470              :   {
    1471         9934 :     return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
    1472              :   }
    1473         6816 :   enum built_in_function builtin_code () const final override
    1474              :   {
    1475         6816 :     return BUILT_IN_STRLEN;
    1476              :   }
    1477              : 
    1478         2841 :   void impl_call_pre (const call_details &cd) const final override
    1479              :   {
    1480         5682 :     if (const svalue *strlen_sval
    1481         2841 :           = cd.check_for_null_terminated_string_arg (0, false, nullptr))
    1482         2825 :       if (strlen_sval->get_kind () != SK_UNKNOWN)
    1483              :         {
    1484         2349 :           cd.maybe_set_lhs (strlen_sval);
    1485         2349 :           return;
    1486              :         }
    1487              : 
    1488              :     /* Use a conjured svalue.  */
    1489          492 :     cd.set_any_lhs_with_defaults ();
    1490              :   }
    1491              : };
    1492              : 
    1493              : /* Factory function, so that kf-analyzer.cc can use this class.  */
    1494              : 
    1495              : std::unique_ptr<known_function>
    1496         3377 : make_kf_strlen ()
    1497              : {
    1498         3377 :   return std::make_unique<kf_strlen> ();
    1499              : }
    1500              : 
    1501              : /* Handler for "strncpy" and "__builtin_strncpy".
    1502              :    See e.g. https://en.cppreference.com/w/c/string/byte/strncpy
    1503              : 
    1504              :      extern char *strncpy (char *dst, const char *src, size_t count);
    1505              : 
    1506              :    Handle this by splitting into two outcomes:
    1507              :    (a) truncated read from "src" of "count" bytes,
    1508              :        writing "count" bytes to "dst"
    1509              :    (b) read from "src" of up to (and including) the null terminator,
    1510              :        where the number of bytes read < "count" bytes,
    1511              :        writing those bytes to "dst", and zero-filling the rest,
    1512              :        up to "count".  */
    1513              : 
    1514        10131 : class kf_strncpy : public builtin_known_function
    1515              : {
    1516              : public:
    1517         1225 :   bool matches_call_types_p (const call_details &cd) const final override
    1518              :   {
    1519         1225 :     return (cd.num_args () == 3
    1520         1225 :             && cd.arg_is_pointer_p (0)
    1521         1225 :             && cd.arg_is_pointer_p (1)
    1522         2450 :             && cd.arg_is_integral_p (2));
    1523              :   }
    1524         1400 :   enum built_in_function builtin_code () const final override
    1525              :   {
    1526         1400 :     return BUILT_IN_STRNCPY;
    1527              :   }
    1528              :   void impl_call_post (const call_details &cd) const final override;
    1529              : };
    1530              : 
    1531              : void
    1532          175 : kf_strncpy::impl_call_post (const call_details &cd) const
    1533              : {
    1534            0 :   class strncpy_call_info : public call_info
    1535              :   {
    1536              :   public:
    1537          350 :     strncpy_call_info (const call_details &cd,
    1538              :                        const svalue *num_bytes_with_terminator_sval,
    1539              :                        bool truncated_read)
    1540          350 :     : call_info (cd),
    1541          350 :       m_num_bytes_with_terminator_sval (num_bytes_with_terminator_sval),
    1542          350 :       m_truncated_read (truncated_read)
    1543              :     {
    1544              :     }
    1545              : 
    1546           76 :     void print_desc (pretty_printer &pp) const final override
    1547              :     {
    1548           76 :       if (m_truncated_read)
    1549            4 :         pp_printf (&pp,
    1550              :                    "when %qE truncates the source string",
    1551              :                    get_fndecl ());
    1552              :       else
    1553           72 :         pp_printf (&pp,
    1554              :                    "when %qE copies the full source string",
    1555              :                    get_fndecl ());
    1556           76 :     }
    1557              : 
    1558          482 :     bool update_model (region_model *model,
    1559              :                        const exploded_edge *,
    1560              :                        region_model_context *ctxt) const final override
    1561              :     {
    1562          482 :       const call_details cd (get_call_details (model, ctxt));
    1563              : 
    1564          482 :       const svalue *dest_sval = cd.get_arg_svalue (0);
    1565          482 :       const region *dest_reg
    1566          482 :         = model->deref_rvalue (dest_sval, cd.get_arg_tree (0), ctxt);
    1567              : 
    1568          482 :       const svalue *src_sval = cd.get_arg_svalue (1);
    1569          482 :       const region *src_reg
    1570          482 :         = model->deref_rvalue (src_sval, cd.get_arg_tree (1), ctxt);
    1571              : 
    1572          482 :       const svalue *count_sval = cd.get_arg_svalue (2);
    1573              : 
    1574              :       /* strncpy returns the initial param.  */
    1575          482 :       cd.maybe_set_lhs (dest_sval);
    1576              : 
    1577          482 :       const svalue *num_bytes_read_sval;
    1578          482 :       if (m_truncated_read)
    1579              :         {
    1580              :           /* Truncated read.  */
    1581          241 :           num_bytes_read_sval = count_sval;
    1582              : 
    1583          241 :           if (m_num_bytes_with_terminator_sval)
    1584              :             {
    1585              :               /* The terminator is after the limit.  */
    1586          211 :               if (!model->add_constraint (m_num_bytes_with_terminator_sval,
    1587              :                                           GT_EXPR,
    1588              :                                           count_sval,
    1589              :                                           ctxt))
    1590              :                 return false;
    1591              :             }
    1592              :           else
    1593              :             {
    1594              :               /* We don't know where the terminator is, or if there is one.
    1595              :                  In theory we know that the first COUNT bytes are non-zero,
    1596              :                  but we don't have a way to record that constraint.  */
    1597              :             }
    1598              :         }
    1599              :       else
    1600              :         {
    1601              :           /* Full read of the src string before reaching the limit,
    1602              :              so there must be a terminator and it must be at or before
    1603              :              the limit.  */
    1604          241 :           if (m_num_bytes_with_terminator_sval)
    1605              :             {
    1606          211 :               if (!model->add_constraint (m_num_bytes_with_terminator_sval,
    1607              :                                           LE_EXPR,
    1608              :                                           count_sval,
    1609              :                                           ctxt))
    1610              :                 return false;
    1611          207 :               num_bytes_read_sval = m_num_bytes_with_terminator_sval;
    1612              : 
    1613              :               /* First, zero-fill the dest buffer.
    1614              :                  We don't need to do this for the truncation case, as
    1615              :                  this fully populates the dest buffer.  */
    1616          207 :               const region *sized_dest_reg
    1617          207 :                 = model->get_manager ()->get_sized_region (dest_reg,
    1618              :                                                            NULL_TREE,
    1619              :                                                            count_sval);
    1620          207 :               model->zero_fill_region (sized_dest_reg, ctxt);
    1621              :             }
    1622              :           else
    1623              :             {
    1624              :               /* Don't analyze this case; the other case will
    1625              :                  assume a "truncated" read up to the limit.  */
    1626              :               return false;
    1627              :             }
    1628              :         }
    1629              : 
    1630          434 :       gcc_assert (num_bytes_read_sval);
    1631              : 
    1632          434 :       const svalue *bytes_to_copy
    1633          434 :         = model->read_bytes (src_reg,
    1634              :                              cd.get_arg_tree (1),
    1635              :                              num_bytes_read_sval,
    1636              :                              ctxt);
    1637          434 :       cd.complain_about_overlap (0, 1, num_bytes_read_sval);
    1638          434 :       model->write_bytes (dest_reg,
    1639              :                           num_bytes_read_sval,
    1640              :                           bytes_to_copy,
    1641              :                           ctxt);
    1642              : 
    1643          434 :       return true;
    1644              :     }
    1645              :   private:
    1646              :     /* (strlen + 1) of the source string if it has a terminator,
    1647              :        or nullptr for the case where UB would happen before
    1648              :        finding any terminator.  */
    1649              :     const svalue *m_num_bytes_with_terminator_sval;
    1650              : 
    1651              :     /* true: if this is the outcome where the limit was reached before
    1652              :        the null terminator
    1653              :        false: if the null terminator was reached before the limit.  */
    1654              :     bool m_truncated_read;
    1655              :   };
    1656              : 
    1657              :   /* Body of kf_strncpy::impl_call_post.  */
    1658          175 :   if (cd.get_ctxt ())
    1659              :     {
    1660              :       /* First, scan for a null terminator as if there were no limit,
    1661              :          with a null ctxt so no errors are reported.  */
    1662          175 :       const region_model *model = cd.get_model ();
    1663          175 :       const svalue *ptr_arg_sval = cd.get_arg_svalue (1);
    1664          175 :       const region *buf_reg
    1665          175 :         = model->deref_rvalue (ptr_arg_sval, cd.get_arg_tree (1), nullptr);
    1666          175 :       const svalue *num_bytes_with_terminator_sval
    1667          175 :         = model->scan_for_null_terminator (buf_reg,
    1668              :                                            cd.get_arg_tree (1),
    1669              :                                            nullptr,
    1670          175 :                                            nullptr);
    1671          175 :       cd.get_ctxt ()->bifurcate
    1672          175 :         (std::make_unique<strncpy_call_info>
    1673          175 :            (cd, num_bytes_with_terminator_sval,
    1674          175 :             false));
    1675          175 :       cd.get_ctxt ()->bifurcate
    1676          175 :         (std::make_unique<strncpy_call_info>
    1677          175 :            (cd, num_bytes_with_terminator_sval,
    1678          175 :             true));
    1679          175 :       cd.get_ctxt ()->terminate_path ();
    1680              :     }
    1681          175 : };
    1682              : 
    1683              : /* Handler for "strndup" and "__builtin_strndup".  */
    1684              : 
    1685         6754 : class kf_strndup : public builtin_known_function
    1686              : {
    1687              : public:
    1688          108 :   bool matches_call_types_p (const call_details &cd) const final override
    1689              :   {
    1690          108 :     return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
    1691              :   }
    1692           84 :   enum built_in_function builtin_code () const final override
    1693              :   {
    1694           84 :     return BUILT_IN_STRNDUP;
    1695              :   }
    1696           26 :   void impl_call_pre (const call_details &cd) const final override
    1697              :   {
    1698           26 :     region_model *model = cd.get_model ();
    1699           26 :     region_model_manager *mgr = cd.get_manager ();
    1700              :     /* Ideally we'd get the size here, and simulate copying the bytes.  */
    1701           26 :     const region *new_reg
    1702           26 :       = model->get_or_create_region_for_heap_alloc (nullptr, cd.get_ctxt ());
    1703           26 :     model->mark_region_as_unknown (new_reg, nullptr);
    1704           26 :     if (cd.get_lhs_type ())
    1705              :       {
    1706           26 :         const svalue *ptr_sval
    1707           26 :           = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
    1708           26 :         cd.maybe_set_lhs (ptr_sval);
    1709              :       }
    1710           26 :   }
    1711              : };
    1712              : 
    1713              : /* Handler for "strstr" and "__builtin_strstr".
    1714              :      extern char *strstr (const char* str, const char* substr);
    1715              :    See e.g. https://en.cppreference.com/w/c/string/byte/strstr  */
    1716              : 
    1717         6754 : class kf_strstr : public builtin_known_function
    1718              : {
    1719              : public:
    1720          279 :   bool matches_call_types_p (const call_details &cd) const final override
    1721              :   {
    1722          279 :     return (cd.num_args () == 2
    1723          279 :             && cd.arg_is_pointer_p (0)
    1724          558 :             && cd.arg_is_pointer_p (1));
    1725              :   }
    1726          328 :   enum built_in_function builtin_code () const final override
    1727              :   {
    1728          328 :     return BUILT_IN_STRSTR;
    1729              :   }
    1730           41 :   void impl_call_pre (const call_details &cd) const final override
    1731              :   {
    1732           41 :     cd.check_for_null_terminated_string_arg (0);
    1733           41 :     cd.check_for_null_terminated_string_arg (1);
    1734           41 :   }
    1735              :   void impl_call_post (const call_details &cd) const final override;
    1736              : };
    1737              : 
    1738              : void
    1739           33 : kf_strstr::impl_call_post (const call_details &cd) const
    1740              : {
    1741            0 :   class strstr_call_info : public call_info
    1742              :   {
    1743              :   public:
    1744           66 :     strstr_call_info (const call_details &cd, bool found)
    1745           66 :     : call_info (cd), m_found (found)
    1746              :     {
    1747              :     }
    1748              : 
    1749            8 :     void print_desc (pretty_printer &pp) const final override
    1750              :     {
    1751            8 :       if (m_found)
    1752            0 :         pp_printf (&pp,
    1753              :                    "when %qE returns non-NULL",
    1754              :                    get_fndecl ());
    1755              :       else
    1756            8 :         pp_printf (&pp,
    1757              :                    "when %qE returns NULL",
    1758              :                    get_fndecl ());
    1759            8 :     }
    1760              : 
    1761           98 :     bool update_model (region_model *model,
    1762              :                        const exploded_edge *,
    1763              :                        region_model_context *ctxt) const final override
    1764              :     {
    1765           98 :       const call_details cd (get_call_details (model, ctxt));
    1766           98 :       if (tree lhs_type = cd.get_lhs_type ())
    1767              :         {
    1768           98 :           region_model_manager *mgr = model->get_manager ();
    1769           98 :           const svalue *result;
    1770           98 :           if (m_found)
    1771              :             {
    1772           33 :               const svalue *str_sval = cd.get_arg_svalue (0);
    1773           33 :               const region *str_reg
    1774           33 :                 = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
    1775              :                                        cd.get_ctxt ());
    1776              :               /* We want str_sval + OFFSET for some unknown OFFSET.
    1777              :                  Use a conjured_svalue to represent the offset,
    1778              :                  using the str_reg as the id of the conjured_svalue.  */
    1779           33 :               const svalue *offset
    1780           33 :                 = mgr->get_or_create_conjured_svalue (size_type_node,
    1781           33 :                                                       &cd.get_call_stmt (),
    1782              :                                                       str_reg,
    1783           33 :                                                       conjured_purge (model,
    1784           33 :                                                                       ctxt));
    1785           33 :               result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR,
    1786              :                                                  str_sval, offset);
    1787              :             }
    1788              :           else
    1789           65 :             result = mgr->get_or_create_int_cst (lhs_type, 0);
    1790           98 :           cd.maybe_set_lhs (result);
    1791              :         }
    1792           98 :       return true;
    1793              :     }
    1794              :   private:
    1795              :     bool m_found;
    1796              :   };
    1797              : 
    1798              :   /* Body of kf_strstr::impl_call_post.  */
    1799           33 :   if (cd.get_ctxt ())
    1800              :     {
    1801           33 :       cd.get_ctxt ()->bifurcate (std::make_unique<strstr_call_info> (cd, false));
    1802           33 :       cd.get_ctxt ()->bifurcate (std::make_unique<strstr_call_info> (cd, true));
    1803           33 :       cd.get_ctxt ()->terminate_path ();
    1804              :     }
    1805           33 : }
    1806              : 
    1807              : /* Handle calls to "strtok".
    1808              :    See e.g.
    1809              :      https://en.cppreference.com/w/c/string/byte/strtok
    1810              :      https://man7.org/linux/man-pages/man3/strtok.3.html  */
    1811              : 
    1812              : class kf_strtok : public known_function
    1813              : {
    1814              : public:
    1815              :   class undefined_behavior : public undefined_function_behavior
    1816              :   {
    1817              :   public:
    1818            8 :     undefined_behavior (const call_details &cd)
    1819            8 :     : undefined_function_behavior (cd)
    1820              :     {
    1821              :     }
    1822           12 :     int get_controlling_option () const final override
    1823              :     {
    1824           12 :       return OPT_Wanalyzer_undefined_behavior_strtok;
    1825              :     }
    1826              : 
    1827            4 :     bool emit (diagnostic_emission_context &ctxt) final override
    1828              :     {
    1829              :       /* CWE-476: NULL Pointer Dereference.  */
    1830            4 :       ctxt.add_cwe (476);
    1831            4 :       if (ctxt.warn ("calling %qD for first time with NULL as argument 1"
    1832              :                      " has undefined behavior",
    1833              :                      get_callee_fndecl ()))
    1834              :         {
    1835            4 :           inform (ctxt.get_location (),
    1836              :                   "some implementations of %qD may crash on such input",
    1837              :                   get_callee_fndecl ());
    1838            4 :           return true;
    1839              :         }
    1840              :       return false;
    1841              :     }
    1842              : 
    1843              :     bool
    1844            8 :     describe_final_event (pretty_printer &pp,
    1845              :                           const evdesc::final_event &) final override
    1846              :     {
    1847            8 :       pp_printf (&pp,
    1848              :                  "calling %qD for first time with NULL as argument 1"
    1849              :                  " has undefined behavior",
    1850              :                  get_callee_fndecl ());
    1851            8 :       return true;
    1852              :     }
    1853              :   };
    1854              : 
    1855              :   /* An outcome of a "strtok" call.
    1856              :      We have a four-way bifurcation of the analysis via the
    1857              :      4 combinations of two flags:
    1858              :      - m_nonnull_str covers whether the "str" param was null or non-null
    1859              :      - m_found covers whether the result is null or non-null
    1860              :    */
    1861              :   class strtok_call_info : public call_info
    1862              :   {
    1863              :   public:
    1864          512 :     strtok_call_info (const call_details &cd,
    1865              :                       const private_region &private_reg,
    1866              :                       bool nonnull_str,
    1867              :                       bool found)
    1868          512 :     : call_info (cd),
    1869          512 :       m_private_reg (private_reg),
    1870          512 :       m_nonnull_str (nonnull_str),
    1871          512 :       m_found (found)
    1872              :     {
    1873              :     }
    1874              : 
    1875            0 :     void print_desc (pretty_printer &pp) const final override
    1876              :     {
    1877            0 :       if (m_nonnull_str)
    1878              :         {
    1879            0 :           if (m_found)
    1880            0 :             pp_printf (&pp,
    1881              :                        "when %qE on non-NULL string returns non-NULL",
    1882              :                        get_fndecl ());
    1883              :           else
    1884            0 :             pp_printf (&pp,
    1885              :                        "when %qE on non-NULL string returns NULL",
    1886              :                        get_fndecl ());
    1887              :         }
    1888              :       else
    1889              :         {
    1890            0 :           if (m_found)
    1891            0 :             pp_printf (&pp,
    1892              :                        "when %qE with NULL string (using prior) returns"
    1893              :                        " non-NULL",
    1894              :                        get_fndecl ());
    1895              :           else
    1896            0 :             pp_printf (&pp,
    1897              :                        "when %qE with NULL string (using prior) returns NULL",
    1898              :                        get_fndecl ());
    1899              :         }
    1900            0 :     }
    1901              : 
    1902          528 :     bool update_model (region_model *model,
    1903              :                        const exploded_edge *,
    1904              :                        region_model_context *ctxt) const final override
    1905              :     {
    1906          528 :       region_model_manager *mgr = model->get_manager ();
    1907          528 :       const call_details cd (get_call_details (model, ctxt));
    1908          528 :       const svalue *str_sval = cd.get_arg_svalue (0);
    1909              :       /* const svalue *delim_sval = cd.get_arg_svalue (1); */
    1910              : 
    1911          528 :       cd.check_for_null_terminated_string_arg (1);
    1912              :       /* We check that either arg 0 or the private region is null
    1913              :          terminated below.  */
    1914              : 
    1915          528 :       const svalue *null_ptr_sval
    1916          528 :         = mgr->get_or_create_null_ptr (cd.get_arg_type (0));;
    1917          800 :       if (!model->add_constraint (str_sval,
    1918          528 :                                   m_nonnull_str ? NE_EXPR : EQ_EXPR,
    1919              :                                   null_ptr_sval,
    1920              :                                   cd.get_ctxt ()))
    1921              :         return false;
    1922              : 
    1923          312 :       if (m_nonnull_str)
    1924              :         {
    1925              :           /* Update internal buffer.  */
    1926           72 :           model->set_value (&m_private_reg,
    1927              :                             mgr->get_or_create_unmergeable (str_sval),
    1928              :                             ctxt);
    1929              :         }
    1930              :       else
    1931              :         {
    1932              :           /* Read from internal buffer.  */
    1933          240 :           str_sval = model->get_store_value (&m_private_reg, ctxt);
    1934              : 
    1935              :           /* The initial value of the private region is NULL when we're
    1936              :              on a path from main.  */
    1937          480 :           if (const initial_svalue *initial_sval
    1938          240 :                 = str_sval->dyn_cast_initial_svalue ())
    1939           56 :             if (initial_sval->get_region () == &m_private_reg
    1940           56 :                 && model->called_from_main_p ())
    1941              :               {
    1942              :                 /* Implementations of strtok do not necessarily check for NULL
    1943              :                    here, and may crash; see PR analyzer/107573.
    1944              :                    Warn for this, if we were definitely passed NULL.  */
    1945           16 :                 if (cd.get_arg_svalue (0)->all_zeroes_p ())
    1946              :                   {
    1947            8 :                     if (ctxt)
    1948            8 :                       ctxt->warn (::std::make_unique<undefined_behavior> (cd));
    1949              :                   }
    1950              : 
    1951              :                 /* Assume that "str" was actually non-null; terminate
    1952              :                    this path.  */
    1953           16 :                 return false;
    1954              :               }
    1955              : 
    1956              :           /* Now assume str_sval is non-null.  */
    1957          224 :           if (!model->add_constraint (str_sval,
    1958              :                                       NE_EXPR,
    1959              :                                       null_ptr_sval,
    1960              :                                       cd.get_ctxt ()))
    1961              :             return false;
    1962              :         }
    1963              : 
    1964          296 :       const region *buf_reg = model->deref_rvalue (str_sval, NULL_TREE, ctxt);
    1965          296 :       model->scan_for_null_terminator (buf_reg,
    1966              :                                        NULL_TREE,
    1967              :                                        nullptr,
    1968              :                                        ctxt);
    1969              : 
    1970          296 :       if (m_found)
    1971              :         {
    1972          148 :           const region *str_reg
    1973          148 :             = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
    1974              :                                    cd.get_ctxt ());
    1975              :           /* We want to figure out the start and nul terminator
    1976              :              for the token.
    1977              :              For each, we want str_sval + OFFSET for some unknown OFFSET.
    1978              :              Use a conjured_svalue to represent the offset,
    1979              :              using the str_reg as the id of the conjured_svalue.  */
    1980          148 :           const svalue *start_offset
    1981          148 :             = mgr->get_or_create_conjured_svalue (size_type_node,
    1982          148 :                                                   &cd.get_call_stmt (),
    1983              :                                                   str_reg,
    1984          148 :                                                   conjured_purge (model,
    1985          148 :                                                                   ctxt),
    1986              :                                                   0);
    1987          148 :           const svalue *nul_offset
    1988          148 :             = mgr->get_or_create_conjured_svalue (size_type_node,
    1989          148 :                                                   &cd.get_call_stmt (),
    1990              :                                                   str_reg,
    1991          148 :                                                   conjured_purge (model,
    1992          148 :                                                                   ctxt),
    1993              :                                                   1);
    1994              : 
    1995          148 :           tree char_ptr_type = build_pointer_type (char_type_node);
    1996          148 :           const svalue *result
    1997          148 :             = mgr->get_or_create_binop (char_ptr_type, POINTER_PLUS_EXPR,
    1998              :                                         str_sval, start_offset);
    1999          148 :           cd.maybe_set_lhs (result);
    2000              : 
    2001              :           /* nul_offset + 1; the offset to use for the next call.  */
    2002          148 :           const svalue *next_offset
    2003          148 :             = mgr->get_or_create_binop (size_type_node, PLUS_EXPR,
    2004              :                                         nul_offset,
    2005              :                                         mgr->get_or_create_int_cst
    2006          148 :                                         (char_type_node, 1));
    2007              : 
    2008              :           /* Write '\0' to str_sval[nul_offset].  */
    2009          148 :           const svalue *ptr_to_term
    2010          148 :             = mgr->get_or_create_binop (char_ptr_type, POINTER_PLUS_EXPR,
    2011              :                                         str_sval, nul_offset);
    2012          148 :           const region *terminator_reg
    2013          148 :             = model->deref_rvalue (ptr_to_term, NULL_TREE, cd.get_ctxt ());
    2014          148 :           model->set_value (terminator_reg,
    2015              :                             mgr->get_or_create_unmergeable
    2016              :                             (mgr->get_or_create_int_cst (char_type_node,
    2017          148 :                                                          0)),
    2018              :                             cd.get_ctxt ());
    2019              : 
    2020              :           /* Update saved ptr to be at [nul_offset + 1].  */
    2021          148 :           const svalue *ptr_to_next
    2022          148 :             = mgr->get_or_create_binop (cd.get_lhs_type (), POINTER_PLUS_EXPR,
    2023              :                                         str_sval, next_offset);
    2024          148 :           model->set_value (&m_private_reg, ptr_to_next, ctxt);
    2025              :         }
    2026              :       else
    2027          148 :         if (tree lhs_type = cd.get_lhs_type ())
    2028              :           {
    2029          140 :             const svalue *result
    2030          140 :               = mgr->get_or_create_int_cst (lhs_type, 0);
    2031          140 :             cd.maybe_set_lhs (result);
    2032              :           }
    2033              :       return true;
    2034              :     }
    2035              :   private:
    2036              :     const private_region &m_private_reg;
    2037              :     bool m_nonnull_str;
    2038              :     bool m_found;
    2039              :   }; // class strtok_call_info
    2040              : 
    2041         6754 :   kf_strtok (region_model_manager &mgr)
    2042         6754 :   : m_private_reg (mgr.alloc_symbol_id (),
    2043         6754 :                    mgr.get_root_region (),
    2044              :                    get_region_type (),
    2045         6754 :                    "strtok buffer")
    2046              :   {
    2047         6754 :   }
    2048              : 
    2049          896 :   bool matches_call_types_p (const call_details &cd) const final override
    2050              :   {
    2051          896 :     return (cd.num_args () == 2
    2052          896 :             && POINTER_TYPE_P (cd.get_arg_type (0))
    2053         1792 :             && POINTER_TYPE_P (cd.get_arg_type (1)));
    2054              :   }
    2055              : 
    2056          128 :   void impl_call_post (const call_details &cd) const final override
    2057              :   {
    2058          128 :     if (cd.get_ctxt ())
    2059              :       {
    2060              :         /* Four-way bifurcation, based on whether:
    2061              :            - the str is non-null
    2062              :            - the result is non-null
    2063              :            Typically the str is either null or non-null at a particular site,
    2064              :            so hopefully this will generally just lead to two out-edges.  */
    2065          128 :         cd.get_ctxt ()->bifurcate
    2066          128 :           (std::make_unique<strtok_call_info> (cd, m_private_reg, false, false));
    2067          128 :         cd.get_ctxt ()->bifurcate
    2068          128 :           (std::make_unique<strtok_call_info> (cd, m_private_reg, false, true));
    2069          128 :         cd.get_ctxt ()->bifurcate
    2070          128 :           (std::make_unique<strtok_call_info> (cd, m_private_reg, true, false));
    2071          128 :         cd.get_ctxt ()->bifurcate
    2072          128 :           (std::make_unique<strtok_call_info> (cd, m_private_reg, true, true));
    2073          128 :         cd.get_ctxt ()->terminate_path ();
    2074              :       }
    2075          128 :   }
    2076              : 
    2077              : private:
    2078         6754 :   static tree get_region_type ()
    2079              :   {
    2080         6754 :     return build_pointer_type (char_type_node);
    2081              :   }
    2082              :   const private_region m_private_reg;
    2083              : };
    2084              : 
    2085              : /* Handle calls to functions referenced by
    2086              :    __attribute__((malloc(FOO))).  */
    2087              : 
    2088              : void
    2089          339 : region_model::impl_deallocation_call (const call_details &cd)
    2090              : {
    2091          339 :   kf_free kf;
    2092          339 :   kf.impl_call_post (cd);
    2093          339 : }
    2094              : 
    2095              : static void
    2096         3377 : register_atomic_builtins (known_function_manager &kfm)
    2097              : {
    2098         3377 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE, std::make_unique<kf_atomic_exchange> ());
    2099         3377 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_N, std::make_unique<kf_atomic_exchange_n> ());
    2100         3377 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_1, std::make_unique<kf_atomic_exchange_n> ());
    2101         3377 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_2, std::make_unique<kf_atomic_exchange_n> ());
    2102         3377 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_4, std::make_unique<kf_atomic_exchange_n> ());
    2103         3377 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_8, std::make_unique<kf_atomic_exchange_n> ());
    2104         3377 :   kfm.add (BUILT_IN_ATOMIC_EXCHANGE_16, std::make_unique<kf_atomic_exchange_n> ());
    2105         3377 :   kfm.add (BUILT_IN_ATOMIC_LOAD, std::make_unique<kf_atomic_load> ());
    2106         3377 :   kfm.add (BUILT_IN_ATOMIC_LOAD_N, std::make_unique<kf_atomic_load_n> ());
    2107         3377 :   kfm.add (BUILT_IN_ATOMIC_LOAD_1, std::make_unique<kf_atomic_load_n> ());
    2108         3377 :   kfm.add (BUILT_IN_ATOMIC_LOAD_2, std::make_unique<kf_atomic_load_n> ());
    2109         3377 :   kfm.add (BUILT_IN_ATOMIC_LOAD_4, std::make_unique<kf_atomic_load_n> ());
    2110         3377 :   kfm.add (BUILT_IN_ATOMIC_LOAD_8, std::make_unique<kf_atomic_load_n> ());
    2111         3377 :   kfm.add (BUILT_IN_ATOMIC_LOAD_16, std::make_unique<kf_atomic_load_n> ());
    2112         3377 :   kfm.add (BUILT_IN_ATOMIC_STORE, std::make_unique<kf_atomic_store> ());
    2113         3377 :   kfm.add (BUILT_IN_ATOMIC_STORE_N, std::make_unique<kf_atomic_store_n> ());
    2114         3377 :   kfm.add (BUILT_IN_ATOMIC_STORE_1, std::make_unique<kf_atomic_store_n> ());
    2115         3377 :   kfm.add (BUILT_IN_ATOMIC_STORE_2, std::make_unique<kf_atomic_store_n> ());
    2116         3377 :   kfm.add (BUILT_IN_ATOMIC_STORE_4, std::make_unique<kf_atomic_store_n> ());
    2117         3377 :   kfm.add (BUILT_IN_ATOMIC_STORE_8, std::make_unique<kf_atomic_store_n> ());
    2118         3377 :   kfm.add (BUILT_IN_ATOMIC_STORE_16, std::make_unique<kf_atomic_store_n> ());
    2119         3377 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_1,
    2120         3377 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2121         3377 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_2,
    2122         3377 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2123         3377 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_4,
    2124         3377 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2125         3377 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_8,
    2126         3377 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2127         3377 :   kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_16,
    2128         3377 :            std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
    2129         3377 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_1,
    2130         3377 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2131         3377 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_2,
    2132         3377 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2133         3377 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_4,
    2134         3377 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2135         3377 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_8,
    2136         3377 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2137         3377 :   kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_16,
    2138         3377 :            std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
    2139         3377 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_1,
    2140         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2141         3377 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_2,
    2142         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2143         3377 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_4,
    2144         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2145         3377 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_8,
    2146         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2147         3377 :   kfm.add (BUILT_IN_ATOMIC_AND_FETCH_16,
    2148         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
    2149         3377 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_1,
    2150         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2151         3377 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_2,
    2152         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2153         3377 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_4,
    2154         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2155         3377 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_8,
    2156         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2157         3377 :   kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_16,
    2158         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
    2159         3377 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_1,
    2160         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2161         3377 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_2,
    2162         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2163         3377 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_4,
    2164         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2165         3377 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_8,
    2166         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2167         3377 :   kfm.add (BUILT_IN_ATOMIC_OR_FETCH_16,
    2168         3377 :            std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
    2169         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_1,
    2170         3377 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2171         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_2,
    2172         3377 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2173         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_4,
    2174         3377 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2175         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_8,
    2176         3377 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2177         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_16,
    2178         3377 :            std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
    2179         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_1,
    2180         3377 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2181         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_2,
    2182         3377 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2183         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_4,
    2184         3377 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2185         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_8,
    2186         3377 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2187         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_16,
    2188         3377 :            std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
    2189         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_1,
    2190         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2191         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_2,
    2192         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2193         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_4,
    2194         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2195         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_8,
    2196         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2197         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_AND_16,
    2198         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
    2199         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_1,
    2200         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    2201         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_2,
    2202         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    2203         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_4,
    2204         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    2205         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_8,
    2206         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    2207         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_16,
    2208         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
    2209         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_1,
    2210         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    2211         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_2,
    2212         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    2213         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_4,
    2214         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    2215         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_8,
    2216         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    2217         3377 :   kfm.add (BUILT_IN_ATOMIC_FETCH_OR_16,
    2218         3377 :            std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
    2219         3377 : }
    2220              : 
    2221              : /* Handle calls to the various IFN_UBSAN_* with no return value.
    2222              :    For now, treat these as no-ops.  */
    2223              : 
    2224        10131 : class kf_ubsan_noop : public internal_known_function
    2225              : {
    2226              : };
    2227              : 
    2228              : /* Handle calls to the various __builtin___ubsan_handle_*.
    2229              :    These can return, but continuing after such a return
    2230              :    isn't likely to be interesting to the user of the analyzer.
    2231              :    Hence we terminate the analysis path at one of these calls.  */
    2232              : 
    2233         3377 : class kf_ubsan_handler : public internal_known_function
    2234              : {
    2235           12 :   void impl_call_post (const call_details &cd) const final override
    2236              :   {
    2237           12 :     if (cd.get_ctxt ())
    2238           12 :       cd.get_ctxt ()->terminate_path ();
    2239           12 :   }
    2240              : };
    2241              : 
    2242              : static void
    2243         3377 : register_sanitizer_builtins (known_function_manager &kfm)
    2244              : {
    2245              :   /* Handle calls to the various IFN_UBSAN_* with no return value.
    2246              :      For now, treat these as no-ops.  */
    2247         3377 :   kfm.add (IFN_UBSAN_NULL,
    2248         3377 :            std::make_unique<kf_ubsan_noop> ());
    2249         3377 :   kfm.add (IFN_UBSAN_BOUNDS,
    2250         3377 :            std::make_unique<kf_ubsan_noop> ());
    2251         3377 :   kfm.add (IFN_UBSAN_PTR,
    2252         3377 :            std::make_unique<kf_ubsan_noop> ());
    2253              : 
    2254         3377 :   kfm.add (BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
    2255         3377 :            std::make_unique<kf_ubsan_handler> ());
    2256         3377 : }
    2257              : 
    2258              : /* Populate KFM with instances of known functions supported by the core of the
    2259              :    analyzer (as opposed to plugins).  */
    2260              : 
    2261              : void
    2262         3377 : register_known_functions (known_function_manager &kfm,
    2263              :                           region_model_manager &rmm)
    2264              : {
    2265              :   /* Debugging/test support functions, all  with a "__analyzer_" prefix.  */
    2266         3377 :   register_known_analyzer_functions (kfm);
    2267              : 
    2268              :   /* Internal fns the analyzer has known_functions for.  */
    2269         3377 :   {
    2270         3377 :     kfm.add (IFN_BUILTIN_EXPECT, std::make_unique<kf_expect> ());
    2271              :   }
    2272              : 
    2273              :   /* GCC built-ins that do not correspond to a function
    2274              :      in the standard library.  */
    2275         3377 :   {
    2276         3377 :     kfm.add (BUILT_IN_EXPECT, std::make_unique<kf_expect> ());
    2277         3377 :     kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, std::make_unique<kf_expect> ());
    2278         3377 :     kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, std::make_unique<kf_alloca> ());
    2279         3377 :     kfm.add (BUILT_IN_STACK_RESTORE, std::make_unique<kf_stack_restore> ());
    2280         3377 :     kfm.add (BUILT_IN_STACK_SAVE, std::make_unique<kf_stack_save> ());
    2281              : 
    2282         3377 :     kfm.add (BUILT_IN_EH_POINTER, std::make_unique<kf_eh_pointer> ());
    2283              : 
    2284         3377 :     register_atomic_builtins (kfm);
    2285         3377 :     register_sanitizer_builtins (kfm);
    2286         3377 :     register_varargs_builtins (kfm);
    2287              :   }
    2288              : 
    2289              :   /* Known builtins and C standard library functions
    2290              :      the analyzer has known functions for.  */
    2291         3377 :   {
    2292         3377 :     kfm.add ("alloca", std::make_unique<kf_alloca> ());
    2293         3377 :     kfm.add ("__builtin_alloca", std::make_unique<kf_alloca> ());
    2294         3377 :     kfm.add ("calloc", std::make_unique<kf_calloc> ());
    2295         3377 :     kfm.add ("__builtin_calloc", std::make_unique<kf_calloc> ());
    2296         3377 :     kfm.add ("free", std::make_unique<kf_free> ());
    2297         3377 :     kfm.add ("__builtin_free", std::make_unique<kf_free> ());
    2298         3377 :     kfm.add ("malloc", std::make_unique<kf_malloc> ());
    2299         3377 :     kfm.add ("__builtin_malloc", std::make_unique<kf_malloc> ());
    2300         3377 :     kfm.add ("memcpy",
    2301         3377 :               std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
    2302         3377 :     kfm.add ("__builtin_memcpy",
    2303         3377 :               std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
    2304         3377 :     kfm.add ("__memcpy_chk", std::make_unique<kf_memcpy_memmove>
    2305         3377 :                               (kf_memcpy_memmove::KF_MEMCPY_CHK));
    2306         3377 :     kfm.add ("__builtin___memcpy_chk", std::make_unique<kf_memcpy_memmove>
    2307         3377 :                               (kf_memcpy_memmove::KF_MEMCPY_CHK));
    2308         3377 :     kfm.add ("memmove",
    2309         3377 :               std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
    2310         3377 :     kfm.add ("__builtin_memmove",
    2311         3377 :               std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
    2312         3377 :     kfm.add ("__memmove_chk", std::make_unique<kf_memcpy_memmove>
    2313         3377 :                               (kf_memcpy_memmove::KF_MEMMOVE_CHK));
    2314         3377 :     kfm.add ("__builtin___memmove_chk", std::make_unique<kf_memcpy_memmove>
    2315         3377 :                               (kf_memcpy_memmove::KF_MEMMOVE_CHK));
    2316         3377 :     kfm.add ("memset", std::make_unique<kf_memset> (false));
    2317         3377 :     kfm.add ("__builtin_memset", std::make_unique<kf_memset> (false));
    2318         3377 :     kfm.add ("__memset_chk", std::make_unique<kf_memset> (true));
    2319         3377 :     kfm.add ("__builtin___memset_chk", std::make_unique<kf_memset> (true));
    2320         3377 :     kfm.add ("realloc", std::make_unique<kf_realloc> ());
    2321         3377 :     kfm.add ("__builtin_realloc", std::make_unique<kf_realloc> ());
    2322         3377 :     kfm.add ("sprintf", std::make_unique<kf_sprintf> ());
    2323         3377 :     kfm.add ("__builtin_sprintf", std::make_unique<kf_sprintf> ());
    2324         3377 :     kfm.add ("strchr", std::make_unique<kf_strchr> ());
    2325         3377 :     kfm.add ("__builtin_strchr", std::make_unique<kf_strchr> ());
    2326         3377 :     kfm.add ("strcpy", std::make_unique<kf_strcpy> (2, false));
    2327         3377 :     kfm.add ("__builtin_strcpy", std::make_unique<kf_strcpy> (2, false));
    2328         3377 :     kfm.add ("__strcpy_chk", std::make_unique<kf_strcpy> (3, true));
    2329         3377 :     kfm.add ("__builtin___strcpy_chk", std::make_unique<kf_strcpy> (3, true));
    2330         3377 :     kfm.add ("strcat", std::make_unique<kf_strcat> (2, false));
    2331         3377 :     kfm.add ("__builtin_strcat", std::make_unique<kf_strcat> (2, false));
    2332         3377 :     kfm.add ("__strcat_chk", std::make_unique<kf_strcat> (3, true));
    2333         3377 :     kfm.add ("__builtin___strcat_chk", std::make_unique<kf_strcat> (3, true));
    2334         3377 :     kfm.add ("strdup", std::make_unique<kf_strdup> ());
    2335         3377 :     kfm.add ("__builtin_strdup", std::make_unique<kf_strdup> ());
    2336         3377 :     kfm.add ("strncpy", std::make_unique<kf_strncpy> ());
    2337         3377 :     kfm.add ("__builtin_strncpy", std::make_unique<kf_strncpy> ());
    2338         3377 :     kfm.add ("strndup", std::make_unique<kf_strndup> ());
    2339         3377 :     kfm.add ("__builtin_strndup", std::make_unique<kf_strndup> ());
    2340         3377 :     kfm.add ("strlen", std::make_unique<kf_strlen> ());
    2341         3377 :     kfm.add ("__builtin_strlen", std::make_unique<kf_strlen> ());
    2342         3377 :     kfm.add ("strstr", std::make_unique<kf_strstr> ());
    2343         3377 :     kfm.add ("__builtin_strstr", std::make_unique<kf_strstr> ());
    2344              :   }
    2345              : 
    2346              :   /* Known POSIX functions, and some non-standard extensions.  */
    2347         3377 :   {
    2348         3377 :     kfm.add ("fopen", std::make_unique<kf_fopen> ());
    2349         3377 :     kfm.add ("putenv", std::make_unique<kf_putenv> ());
    2350         3377 :     kfm.add ("strtok", std::make_unique<kf_strtok> (rmm));
    2351              : 
    2352         3377 :     register_known_fd_functions (kfm);
    2353         3377 :     register_known_file_functions (kfm);
    2354              :   }
    2355              : 
    2356              :   /* glibc functions.  */
    2357         3377 :   {
    2358         3377 :     kfm.add ("__errno_location", std::make_unique<kf_errno_location> ());
    2359         3377 :     kfm.add ("error", std::make_unique<kf_error> (3));
    2360         3377 :     kfm.add ("error_at_line", std::make_unique<kf_error> (5));
    2361              :     /* Variants of "error" and "error_at_line" seen by the
    2362              :        analyzer at -O0 (PR analyzer/115724).  */
    2363         3377 :     kfm.add ("__error_alias", std::make_unique<kf_error> (3));
    2364         3377 :     kfm.add ("__error_at_line_alias", std::make_unique<kf_error> (5));
    2365              :   }
    2366              : 
    2367              :   /* Other implementations of C standard library.  */
    2368         3377 :   {
    2369              :     /* According to PR 107807 comment #2, Solaris implements "errno"
    2370              :        like this:
    2371              :          extern int *___errno(void) __attribute__((__const__));
    2372              :          #define errno (*(___errno()))
    2373              :        and macOS like this:
    2374              :          extern int * __error(void);
    2375              :          #define errno (*__error())
    2376              :        and similarly __errno for newlib.
    2377              :        Add these as synonyms for "__errno_location".  */
    2378         3377 :     kfm.add ("___errno", std::make_unique<kf_errno_location> ());
    2379         3377 :     kfm.add ("__error", std::make_unique<kf_errno_location> ());
    2380         3377 :     kfm.add ("__errno", std::make_unique<kf_errno_location> ());
    2381         3377 :     kfm.add ("__get_errno_ptr", std::make_unique<kf_errno_location> ());
    2382              :   }
    2383              : 
    2384              :   /* Language-specific support functions.  */
    2385         3377 :   register_known_functions_lang_cp (kfm);
    2386              : 
    2387              :   /* Some C++ implementations use the std:: copies of these functions
    2388              :      from <cstdlib> etc for the C spellings of these headers (e.g. <stdlib.h>),
    2389              :      so we must match against these too.  */
    2390         3377 :   {
    2391         3377 :     kfm.add_std_ns ("malloc", std::make_unique<kf_malloc> ());
    2392         3377 :     kfm.add_std_ns ("free", std::make_unique<kf_free> ());
    2393         3377 :     kfm.add_std_ns ("realloc", std::make_unique<kf_realloc> ());
    2394         3377 :     kfm.add_std_ns ("calloc", std::make_unique<kf_calloc> ());
    2395         3377 :     kfm.add_std_ns
    2396         3377 :       ("memcpy",
    2397         3377 :        std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
    2398         3377 :     kfm.add_std_ns
    2399         3377 :       ("memmove",
    2400         3377 :        std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
    2401         3377 :     kfm.add_std_ns ("memset", std::make_unique<kf_memset> (false));
    2402         3377 :     kfm.add_std_ns ("strcat", std::make_unique<kf_strcat> (2, false));
    2403         3377 :     kfm.add_std_ns ("strcpy", std::make_unique<kf_strcpy> (2, false));
    2404         3377 :     kfm.add_std_ns ("strlen", std::make_unique<kf_strlen> ());
    2405         3377 :     kfm.add_std_ns ("strncpy", std::make_unique<kf_strncpy> ());
    2406         3377 :     kfm.add_std_ns ("strtok", std::make_unique<kf_strtok> (rmm));
    2407              :   }
    2408         3377 : }
    2409              : 
    2410              : } // namespace ana
    2411              : 
    2412              : #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.