LCOV - code coverage report
Current view: top level - gcc/analyzer - sm-fd.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 94.7 % 1193 1130
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 110 110
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* A state machine for detecting misuses of POSIX file descriptor APIs.
       2              :    Copyright (C) 2019-2026 Free Software Foundation, Inc.
       3              :    Contributed by Immad Mir <mir@sourceware.org>.
       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 "diagnostics/event-id.h"
      24              : #include "stringpool.h"
      25              : #include "attribs.h"
      26              : 
      27              : #include "analyzer/analyzer-logging.h"
      28              : #include "analyzer/sm.h"
      29              : #include "analyzer/pending-diagnostic.h"
      30              : #include "analyzer/function-set.h"
      31              : #include "analyzer/analyzer-selftests.h"
      32              : #include "analyzer/call-string.h"
      33              : #include "analyzer/program-point.h"
      34              : #include "analyzer/store.h"
      35              : #include "analyzer/region-model.h"
      36              : #include "analyzer/program-state.h"
      37              : #include "analyzer/supergraph.h"
      38              : #include "analyzer/analyzer-language.h"
      39              : #include "analyzer/call-details.h"
      40              : #include "analyzer/call-info.h"
      41              : 
      42              : #if ENABLE_ANALYZER
      43              : 
      44              : namespace ana {
      45              : 
      46              : namespace {
      47              : 
      48              : /* An enum for distinguishing between three different access modes.  */
      49              : 
      50              : enum access_mode
      51              : {
      52              :   READ_WRITE,
      53              :   READ_ONLY,
      54              :   WRITE_ONLY
      55              : };
      56              : 
      57              : enum access_directions
      58              : {
      59              :   DIRS_READ_WRITE,
      60              :   DIRS_READ,
      61              :   DIRS_WRITE
      62              : };
      63              : 
      64              : /* An enum for distinguishing between dup, dup2 and dup3.  */
      65              : enum dup
      66              : {
      67              :   DUP_1,
      68              :   DUP_2,
      69              :   DUP_3
      70              : };
      71              : 
      72              : /* Enum for use by -Wanalyzer-fd-phase-mismatch.  */
      73              : 
      74              : enum expected_phase
      75              : {
      76              :   EXPECTED_PHASE_CAN_TRANSFER, /* can "read"/"write".  */
      77              :   EXPECTED_PHASE_CAN_BIND,
      78              :   EXPECTED_PHASE_CAN_LISTEN,
      79              :   EXPECTED_PHASE_CAN_ACCEPT,
      80              :   EXPECTED_PHASE_CAN_CONNECT
      81              : };
      82              : 
      83              : class fd_state_machine : public state_machine
      84              : {
      85              : public:
      86              :   fd_state_machine (logger *logger);
      87              : 
      88              :   bool
      89      1430797 :   inherited_state_p () const final override
      90              :   {
      91      1430797 :     return false;
      92              :   }
      93              : 
      94              :   state_machine::state_t
      95      1429727 :   get_default_state (const svalue *sval) const final override
      96              :   {
      97      1429727 :     if (tree cst = sval->maybe_get_constant ())
      98              :       {
      99       375552 :         if (TREE_CODE (cst) == INTEGER_CST)
     100              :           {
     101       373898 :             int val = TREE_INT_CST_LOW (cst);
     102       373898 :             if (val >= 0)
     103       367703 :               return m_constant_fd;
     104              :             else
     105         6195 :               return m_invalid;
     106              :           }
     107              :       }
     108      1055829 :     return m_start;
     109              :   }
     110              : 
     111              :   bool on_stmt (sm_context &sm_ctxt,
     112              :                 const gimple *stmt) const final override;
     113              : 
     114              :   void on_condition (sm_context &sm_ctxt,
     115              :                      const svalue *lhs, const tree_code op,
     116              :                      const svalue *rhs) const final override;
     117              : 
     118              :   bool can_purge_p (state_t s) const final override;
     119              : 
     120              :   std::unique_ptr<pending_diagnostic>
     121              :   on_leak (tree var,
     122              :            const program_state *old_state,
     123              :            const program_state *new_state) const final override;
     124              : 
     125              :   bool is_unchecked_fd_p (state_t s) const;
     126              :   bool is_valid_fd_p (state_t s) const;
     127              :   bool is_socket_fd_p (state_t s) const;
     128              :   bool is_datagram_socket_fd_p (state_t s) const;
     129              :   bool is_stream_socket_fd_p (state_t s) const;
     130              :   bool is_closed_fd_p (state_t s) const;
     131              :   bool is_constant_fd_p (state_t s) const;
     132              :   bool is_readonly_fd_p (state_t s) const;
     133              :   bool is_writeonly_fd_p (state_t s) const;
     134              :   enum access_mode get_access_mode_from_flag (int flag) const;
     135              :   /* Function for one-to-one correspondence between valid
     136              :      and unchecked states.  */
     137              :   state_t valid_to_unchecked_state (state_t state) const;
     138              : 
     139              :   void mark_as_valid_fd (region_model *model,
     140              :                          sm_state_map *smap,
     141              :                          const svalue *fd_sval,
     142              :                          const extrinsic_state &ext_state) const;
     143              : 
     144              :   bool on_socket (const call_details &cd,
     145              :                   bool successful,
     146              :                   sm_context &sm_ctxt,
     147              :                   const extrinsic_state &ext_state) const;
     148              :   bool on_bind (const call_details &cd,
     149              :                 bool successful,
     150              :                 sm_context &sm_ctxt,
     151              :                 const extrinsic_state &ext_state) const;
     152              :   bool on_listen (const call_details &cd,
     153              :                   bool successful,
     154              :                   sm_context &sm_ctxt,
     155              :                   const extrinsic_state &ext_state) const;
     156              :   bool on_accept (const call_details &cd,
     157              :                   bool successful,
     158              :                   sm_context &sm_ctxt,
     159              :                   const extrinsic_state &ext_state) const;
     160              :   bool on_connect (const call_details &cd,
     161              :                    bool successful,
     162              :                    sm_context &sm_ctxt,
     163              :                    const extrinsic_state &ext_state) const;
     164              : 
     165              :   /* State for a constant file descriptor (>= 0) */
     166              :   state_t m_constant_fd;
     167              : 
     168              :   /* States representing a file descriptor that hasn't yet been
     169              :     checked for validity after opening, for three different
     170              :     access modes.  */
     171              :   state_t m_unchecked_read_write;
     172              : 
     173              :   state_t m_unchecked_read_only;
     174              : 
     175              :   state_t m_unchecked_write_only;
     176              : 
     177              :   /* States for representing a file descriptor that is known to be valid (>=
     178              :     0), for three different access modes.  */
     179              :   state_t m_valid_read_write;
     180              : 
     181              :   state_t m_valid_read_only;
     182              : 
     183              :   state_t m_valid_write_only;
     184              : 
     185              :   /* State for a file descriptor that is known to be invalid (< 0). */
     186              :   state_t m_invalid;
     187              : 
     188              :   /* State for a file descriptor that has been closed.  */
     189              :   state_t m_closed;
     190              : 
     191              :   /* States for FDs relating to socket APIs.  */
     192              : 
     193              :   /* Result of successful "socket" with SOCK_DGRAM.  */
     194              :   state_t m_new_datagram_socket;
     195              :   /* Result of successful "socket" with SOCK_STREAM.  */
     196              :   state_t m_new_stream_socket;
     197              :   /* Result of successful "socket" with unknown type.  */
     198              :   state_t m_new_unknown_socket;
     199              : 
     200              :   /* The above after a successful call to "bind".  */
     201              :   state_t m_bound_datagram_socket;
     202              :   state_t m_bound_stream_socket;
     203              :   state_t m_bound_unknown_socket;
     204              : 
     205              :   /* A bound socket after a successful call to "listen" (stream or unknown).  */
     206              :   state_t m_listening_stream_socket;
     207              : 
     208              :   /* (i) the new FD as a result of a succesful call to "accept" on a
     209              :      listening socket (via a passive open), or
     210              :      (ii) an active socket after a successful call to "connect"
     211              :      (via an active open).  */
     212              :   state_t m_connected_stream_socket;
     213              : 
     214              :   /* State for a file descriptor that we do not want to track anymore . */
     215              :   state_t m_stop;
     216              : 
     217              :   /* Stashed constant values from the frontend.  These could be NULL_TREE.  */
     218              :   tree m_O_ACCMODE;
     219              :   tree m_O_RDONLY;
     220              :   tree m_O_WRONLY;
     221              :   tree m_SOCK_STREAM;
     222              :   tree m_SOCK_DGRAM;
     223              : 
     224              : private:
     225              :   void on_open (sm_context &sm_ctxt,
     226              :                 const gcall &call) const;
     227              :   void on_creat (sm_context &sm_ctxt,
     228              :                 const gcall &call) const;
     229              :   void on_close (sm_context &sm_ctxt,
     230              :                  const gcall &call) const;
     231              :   void on_read (sm_context &sm_ctxt,
     232              :                 const gcall &call, const tree callee_fndecl) const;
     233              :   void on_write (sm_context &sm_ctxt,
     234              :                  const gcall &call, const tree callee_fndecl) const;
     235              :   void check_for_open_fd (sm_context &sm_ctxt,
     236              :                           const gcall &call,
     237              :                           const tree callee_fndecl,
     238              :                           enum access_directions access_fn) const;
     239              : 
     240              :   void make_valid_transitions_on_condition (sm_context &sm_ctxt,
     241              :                                             const svalue *lhs) const;
     242              :   void make_invalid_transitions_on_condition (sm_context &sm_ctxt,
     243              :                                               const svalue *lhs) const;
     244              :   void check_for_fd_attrs (sm_context &sm_ctxt,
     245              :                            const gcall &call,
     246              :                            const tree callee_fndecl,
     247              :                            const char *attr_name,
     248              :                            access_directions fd_attr_access_dir) const;
     249              :   void check_for_dup (sm_context &sm_ctxt,
     250              :                       const gcall &call,
     251              :                       const tree callee_fndecl,
     252              :                       enum dup kind) const;
     253              : 
     254              :   state_t get_state_for_socket_type (const svalue *socket_type_sval) const;
     255              : 
     256              :   bool check_for_socket_fd (const call_details &cd,
     257              :                             bool successful,
     258              :                             sm_context &sm_ctxt,
     259              :                             const svalue *fd_sval,
     260              :                             state_t old_state,
     261              :                             bool *complained = nullptr) const;
     262              :   bool check_for_new_socket_fd (const call_details &cd,
     263              :                                 bool successful,
     264              :                                 sm_context &sm_ctxt,
     265              :                                 const svalue *fd_sval,
     266              :                                 state_t old_state,
     267              :                                 enum expected_phase expected_phase) const;
     268              : };
     269              : 
     270              : /* Base diagnostic class relative to fd_state_machine.  */
     271            0 : class fd_diagnostic : public pending_diagnostic
     272              : {
     273              : public:
     274          332 :   fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg)
     275              :   {
     276              :   }
     277              : 
     278              :   bool
     279          160 :   subclass_equal_p (const pending_diagnostic &base_other) const override
     280              :   {
     281          160 :     return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg);
     282              :   }
     283              : 
     284              :   bool
     285          258 :   describe_state_change (pretty_printer &pp,
     286              :                          const evdesc::state_change &change) override
     287              :   {
     288          258 :     if (change.m_old_state == m_sm.get_start_state ())
     289              :       {
     290          164 :         if (change.m_new_state == m_sm.m_unchecked_read_write
     291          164 :             || change.m_new_state == m_sm.m_valid_read_write)
     292              :           {
     293            8 :             pp_string (&pp, "opened here as read-write");
     294            8 :             return true;
     295              :           }
     296              : 
     297          156 :         if (change.m_new_state == m_sm.m_unchecked_read_only
     298          144 :             || change.m_new_state == m_sm.m_valid_read_only)
     299              :           {
     300           12 :             pp_string (&pp, "opened here as read-only");
     301           12 :             return true;
     302              :           }
     303              : 
     304          144 :         if (change.m_new_state == m_sm.m_unchecked_write_only
     305          130 :             || change.m_new_state == m_sm.m_valid_write_only)
     306              :           {
     307           14 :             pp_string (&pp, "opened here as write-only");
     308           14 :             return true;
     309              :           }
     310              : 
     311          130 :         if (change.m_new_state == m_sm.m_new_datagram_socket)
     312              :           {
     313           18 :             pp_string (&pp, "datagram socket created here");
     314           18 :             return true;
     315              :           }
     316              : 
     317          112 :         if (change.m_new_state == m_sm.m_new_stream_socket)
     318              :           {
     319           22 :             pp_string (&pp, "stream socket created here");
     320           22 :             return true;
     321              :           }
     322              : 
     323           90 :         if (change.m_new_state == m_sm.m_new_unknown_socket
     324           20 :             || change.m_new_state == m_sm.m_connected_stream_socket)
     325              :           {
     326           80 :             pp_string (&pp, "socket created here");
     327           80 :             return true;
     328              :           }
     329              :       }
     330              : 
     331          104 :     if (change.m_new_state == m_sm.m_bound_datagram_socket)
     332              :       {
     333            4 :         pp_string (&pp, "datagram socket bound here");
     334            4 :         return true;
     335              :       }
     336              : 
     337          100 :     if (change.m_new_state == m_sm.m_bound_stream_socket)
     338              :       {
     339            4 :         pp_string (&pp, "stream socket bound here");
     340            4 :         return true;
     341              :       }
     342              : 
     343           96 :     if (change.m_new_state == m_sm.m_bound_unknown_socket
     344           86 :         || change.m_new_state == m_sm.m_connected_stream_socket)
     345              :       {
     346           10 :         pp_string (&pp, "socket bound here");
     347           10 :         return true;
     348              :       }
     349              : 
     350           86 :     if (change.m_new_state == m_sm.m_listening_stream_socket)
     351              :       {
     352            8 :         pp_printf (&pp,
     353              :                    "stream socket marked as passive here via %qs",
     354              :                    "listen");
     355            8 :         return true;
     356              :       }
     357              : 
     358           78 :     if (change.m_new_state == m_sm.m_closed)
     359              :       {
     360            0 :         pp_string (&pp, "closed here");
     361            0 :         return true;
     362              :       }
     363              : 
     364          164 :     if (m_sm.is_unchecked_fd_p (change.m_old_state)
     365          156 :         && m_sm.is_valid_fd_p (change.m_new_state))
     366              :       {
     367           70 :         if (change.m_expr)
     368           70 :           pp_printf (&pp,
     369              :                      "assuming %qE is a valid file descriptor (>= 0)",
     370              :                      change.m_expr);
     371              :         else
     372            0 :           pp_string (&pp, "assuming a valid file descriptor");
     373           70 :         return true;
     374              :       }
     375              : 
     376          274 :     if (m_sm.is_unchecked_fd_p (change.m_old_state)
     377            8 :         && change.m_new_state == m_sm.m_invalid)
     378              :       {
     379            8 :         if (change.m_expr)
     380            8 :           pp_printf (&pp,
     381              :                      "assuming %qE is an invalid file descriptor (< 0)",
     382              :                      change.m_expr);
     383              :         else
     384            0 :           pp_string (&pp, "assuming an invalid file descriptor");
     385            8 :         return true;
     386              :       }
     387              : 
     388              :     return false;
     389              :   }
     390              : 
     391              :   diagnostics::paths::event::meaning
     392           96 :   get_meaning_for_state_change (
     393              :       const evdesc::state_change &change) const final override
     394              :   {
     395           96 :     using event = diagnostics::paths::event;
     396           96 :     if (change.m_old_state == m_sm.get_start_state ()
     397           96 :         && (m_sm.is_unchecked_fd_p (change.m_new_state)
     398           24 :             || change.m_new_state == m_sm.m_new_datagram_socket
     399           22 :             || change.m_new_state == m_sm.m_new_stream_socket
     400           20 :             || change.m_new_state == m_sm.m_new_unknown_socket))
     401           48 :       return event::meaning (event::verb::acquire,
     402           48 :                              event::noun::resource);
     403           48 :     if (change.m_new_state == m_sm.m_closed)
     404           24 :       return event::meaning (event::verb::release,
     405           24 :                              event::noun::resource);
     406           24 :     return event::meaning ();
     407              :   }
     408              : 
     409              : protected:
     410              :   const fd_state_machine &m_sm;
     411              :   tree m_arg;
     412              : };
     413              : 
     414            0 : class fd_param_diagnostic : public fd_diagnostic
     415              : {
     416              : public:
     417           12 :   fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl,
     418              :                        const char *attr_name, int arg_idx)
     419           12 :       : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
     420           12 :         m_attr_name (attr_name), m_arg_idx (arg_idx)
     421              :   {
     422              :   }
     423              : 
     424          160 :   fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl)
     425          160 :       : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
     426          160 :         m_attr_name (nullptr), m_arg_idx (-1)
     427              :   {
     428              :   }
     429              : 
     430              :   bool
     431          156 :   subclass_equal_p (const pending_diagnostic &base_other) const override
     432              :   {
     433          156 :     const fd_param_diagnostic &sub_other
     434              :         = (const fd_param_diagnostic &)base_other;
     435          156 :     return (same_tree_p (m_arg, sub_other.m_arg)
     436          156 :             && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl)
     437          156 :             && m_arg_idx == sub_other.m_arg_idx
     438          312 :             && ((m_attr_name)
     439           12 :                     ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0)
     440          156 :                     : true));
     441              :   }
     442              : 
     443              :   void
     444           59 :   inform_filedescriptor_attribute (access_directions fd_dir)
     445              :   {
     446              : 
     447           59 :     if (m_attr_name)
     448           12 :       switch (fd_dir)
     449              :         {
     450            6 :         case DIRS_READ_WRITE:
     451           12 :           inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
     452              :                   "argument %d of %qD must be an open file descriptor, due to "
     453              :                   "%<__attribute__((%s(%d)))%>",
     454            6 :                   m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
     455            6 :           break;
     456            3 :         case DIRS_WRITE:
     457            6 :           inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
     458              :                   "argument %d of %qD must be a readable file descriptor, due "
     459              :                   "to %<__attribute__((%s(%d)))%>",
     460            3 :                   m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
     461            3 :           break;
     462            3 :         case DIRS_READ:
     463            6 :           inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
     464              :                   "argument %d of %qD must be a writable file descriptor, due "
     465              :                   "to %<__attribute__((%s(%d)))%>",
     466            3 :                   m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
     467            3 :           break;
     468              :         }
     469           59 :   }
     470              : 
     471              : protected:
     472              :   tree m_callee_fndecl;
     473              :   const char *m_attr_name;
     474              :   /* ARG_IDX is 0-based.  */
     475              :   int m_arg_idx;
     476              : };
     477              : 
     478              : class fd_leak : public fd_diagnostic
     479              : {
     480              : public:
     481          132 :   fd_leak (const fd_state_machine &sm, tree arg,
     482              :            const program_state *final_state)
     483          132 :   : fd_diagnostic (sm, arg),
     484          117 :     m_final_state ()
     485              :   {
     486          117 :     if (final_state)
     487          117 :       m_final_state = std::make_unique<program_state> (*final_state);
     488          117 :   }
     489              : 
     490              :   const char *
     491         1234 :   get_kind () const final override
     492              :   {
     493         1234 :     return "fd_leak";
     494              :   }
     495              : 
     496              :   int
     497          206 :   get_controlling_option () const final override
     498              :   {
     499          206 :     return OPT_Wanalyzer_fd_leak;
     500              :   }
     501              : 
     502              :   bool
     503           74 :   emit (diagnostic_emission_context &ctxt) final override
     504              :   {
     505              :     /*CWE-775: Missing Release of File Descriptor or Handle after Effective
     506              :       Lifetime
     507              :      */
     508           74 :     ctxt.add_cwe (775);
     509           74 :     if (m_arg)
     510           58 :       return ctxt.warn ("leak of file descriptor %qE", m_arg);
     511              :     else
     512           16 :       return ctxt.warn ("leak of file descriptor");
     513              :   }
     514              : 
     515              :   bool
     516          138 :   describe_state_change (pretty_printer &pp,
     517              :                          const evdesc::state_change &change) final override
     518              :   {
     519          276 :     if (m_sm.is_unchecked_fd_p (change.m_new_state))
     520              :       {
     521           34 :         m_open_event = change.m_event_id;
     522           34 :         pp_string (&pp, "opened here");
     523           34 :         return true;
     524              :       }
     525              : 
     526          104 :     return fd_diagnostic::describe_state_change (pp, change);
     527              :   }
     528              : 
     529              :   bool
     530          148 :   describe_final_event (pretty_printer &pp,
     531              :                         const evdesc::final_event &ev) final override
     532              :   {
     533          148 :     if (m_open_event.known_p ())
     534              :       {
     535           34 :         if (ev.m_expr)
     536           34 :           pp_printf (&pp,
     537              :                      "%qE leaks here; was opened at %@",
     538              :                      ev.m_expr, &m_open_event);
     539              :         else
     540            0 :           pp_printf (&pp,
     541              :                      "leaks here; was opened at %@",
     542              :                      &m_open_event);
     543              :       }
     544              :     else
     545              :       {
     546          114 :         if (ev.m_expr)
     547           82 :           pp_printf (&pp, "%qE leaks here", ev.m_expr);
     548              :         else
     549           32 :           pp_string (&pp, "leaks here");
     550              :       }
     551          148 :     return true;
     552              :   }
     553              : 
     554              :   const program_state *
     555           74 :   get_final_state () const final override
     556              :   {
     557           74 :     return m_final_state.get ();
     558              :   }
     559              : 
     560              : private:
     561              :   diagnostics::paths::event_id_t m_open_event;
     562              :   std::unique_ptr<program_state> m_final_state;
     563              : };
     564              : 
     565            0 : class fd_access_mode_mismatch : public fd_param_diagnostic
     566              : {
     567              : public:
     568            6 :   fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
     569              :                            enum access_directions fd_dir,
     570              :                            const tree callee_fndecl, const char *attr_name,
     571              :                            int arg_idx)
     572            6 :       : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx),
     573            6 :         m_fd_dir (fd_dir)
     574              : 
     575              :   {
     576              :   }
     577              : 
     578            6 :   fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
     579              :                            enum access_directions fd_dir,
     580              :                            const tree callee_fndecl)
     581            6 :       : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir)
     582              :   {
     583              :   }
     584              : 
     585              :   const char *
     586          198 :   get_kind () const final override
     587              :   {
     588          198 :     return "fd_access_mode_mismatch";
     589              :   }
     590              : 
     591              :   int
     592           24 :   get_controlling_option () const final override
     593              :   {
     594           24 :     return OPT_Wanalyzer_fd_access_mode_mismatch;
     595              :   }
     596              : 
     597              :   bool
     598           12 :   emit (diagnostic_emission_context &ctxt) final override
     599              :   {
     600           12 :     bool warned;
     601           12 :     switch (m_fd_dir)
     602              :       {
     603            5 :       case DIRS_READ:
     604            5 :         warned =  ctxt.warn ("%qE on read-only file descriptor %qE",
     605              :                              m_callee_fndecl, m_arg);
     606            5 :         break;
     607            7 :       case DIRS_WRITE:
     608            7 :         warned = ctxt.warn ("%qE on write-only file descriptor %qE",
     609              :                             m_callee_fndecl, m_arg);
     610            7 :         break;
     611            0 :       default:
     612            0 :         gcc_unreachable ();
     613              :       }
     614           12 :       if (warned)
     615           12 :         inform_filedescriptor_attribute (m_fd_dir);
     616           12 :       return warned;
     617              :   }
     618              : 
     619              :   bool
     620           24 :   describe_final_event (pretty_printer &pp,
     621              :                         const evdesc::final_event &) final override
     622              :   {
     623           24 :     switch (m_fd_dir)
     624              :       {
     625           10 :       case DIRS_READ:
     626           10 :         pp_printf (&pp,
     627              :                    "%qE on read-only file descriptor %qE",
     628              :                    m_callee_fndecl, m_arg);
     629           10 :         return true;
     630           14 :       case DIRS_WRITE:
     631           14 :         pp_printf (&pp,
     632              :                    "%qE on write-only file descriptor %qE",
     633              :                    m_callee_fndecl, m_arg);
     634           14 :         return true;
     635            0 :       default:
     636            0 :         gcc_unreachable ();
     637              :       }
     638              :   }
     639              : 
     640              : private:
     641              :   enum access_directions m_fd_dir;
     642              : };
     643              : 
     644            0 : class fd_double_close : public fd_diagnostic
     645              : {
     646              : public:
     647           28 :   fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)
     648              :   {
     649              :   }
     650              : 
     651              :   const char *
     652          248 :   get_kind () const final override
     653              :   {
     654          248 :     return "fd_double_close";
     655              :   }
     656              : 
     657              :   int
     658           56 :   get_controlling_option () const final override
     659              :   {
     660           56 :     return OPT_Wanalyzer_fd_double_close;
     661              :   }
     662              :   bool
     663           28 :   emit (diagnostic_emission_context &ctxt) final override
     664              :   {
     665              :     // CWE-1341: Multiple Releases of Same Resource or Handle
     666           28 :     ctxt.add_cwe (1341);
     667           28 :     return ctxt.warn ("double %<close%> of file descriptor %qE", m_arg);
     668              :   }
     669              : 
     670              :   bool
     671          136 :   describe_state_change (pretty_printer &pp,
     672              :                          const evdesc::state_change &change) override
     673              :   {
     674          272 :     if (m_sm.is_unchecked_fd_p (change.m_new_state))
     675              :       {
     676           48 :         pp_string (&pp, "opened here");
     677           48 :         return true;
     678              :       }
     679              : 
     680           88 :     if (change.m_new_state == m_sm.m_closed)
     681              :       {
     682           56 :         m_first_close_event = change.m_event_id;
     683           56 :         pp_printf (&pp, "first %qs here", "close");
     684           56 :         return true;
     685              :       }
     686           32 :     return fd_diagnostic::describe_state_change (pp, change);
     687              :   }
     688              : 
     689              :   bool
     690           56 :   describe_final_event (pretty_printer &pp,
     691              :                         const evdesc::final_event &) final override
     692              :   {
     693           56 :     if (m_first_close_event.known_p ())
     694           56 :       pp_printf (&pp,
     695              :                  "second %qs here; first %qs was at %@",
     696              :                  "close", "close", &m_first_close_event);
     697              :     else
     698            0 :       pp_printf (&pp,
     699              :                  "second %qs here",
     700              :                  "close");
     701           56 :     return true;
     702              :   }
     703              : 
     704              : private:
     705              :   diagnostics::paths::event_id_t m_first_close_event;
     706              : };
     707              : 
     708            0 : class fd_use_after_close : public fd_param_diagnostic
     709              : {
     710              : public:
     711            3 :   fd_use_after_close (const fd_state_machine &sm, tree arg,
     712              :                       const tree callee_fndecl, const char *attr_name,
     713              :                       int arg_idx)
     714            3 :       : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
     715              :   {
     716              :   }
     717              : 
     718           12 :   fd_use_after_close (const fd_state_machine &sm, tree arg,
     719              :                       const tree callee_fndecl)
     720           12 :       : fd_param_diagnostic (sm, arg, callee_fndecl)
     721              :   {
     722              :   }
     723              : 
     724              :   const char *
     725          129 :   get_kind () const final override
     726              :   {
     727          129 :     return "fd_use_after_close";
     728              :   }
     729              : 
     730              :   int
     731           22 :   get_controlling_option () const final override
     732              :   {
     733           22 :     return OPT_Wanalyzer_fd_use_after_close;
     734              :   }
     735              : 
     736              :   bool
     737            7 :   emit (diagnostic_emission_context &ctxt) final override
     738              :   {
     739            7 :     bool warned = ctxt.warn ("%qE on closed file descriptor %qE",
     740              :                              m_callee_fndecl, m_arg);
     741            7 :     if (warned)
     742            7 :       inform_filedescriptor_attribute (DIRS_READ_WRITE);
     743            7 :     return warned;
     744              :   }
     745              : 
     746              :   bool
     747           32 :   describe_state_change (pretty_printer &pp,
     748              :                          const evdesc::state_change &change) override
     749              :   {
     750           64 :     if (m_sm.is_unchecked_fd_p (change.m_new_state))
     751              :       {
     752           14 :         pp_string (&pp, "opened here");
     753           14 :         return true;
     754              :       }
     755              : 
     756           18 :     if (change.m_new_state == m_sm.m_closed)
     757              :       {
     758           14 :         m_first_close_event = change.m_event_id;
     759           14 :         pp_string (&pp, "closed here");
     760           14 :         return true;
     761              :       }
     762              : 
     763            4 :     return fd_diagnostic::describe_state_change (pp, change);
     764              :   }
     765              : 
     766              :   bool
     767           14 :   describe_final_event (pretty_printer &pp,
     768              :                         const evdesc::final_event &) final override
     769              :   {
     770           14 :     if (m_first_close_event.known_p ())
     771           14 :       pp_printf (&pp,
     772              :                  "%qE on closed file descriptor %qE;"
     773              :                  " %qs was at %@",
     774              :                  m_callee_fndecl, m_arg,
     775              :                  "close", &m_first_close_event);
     776              :     else
     777            0 :       pp_printf (&pp,
     778              :                  "%qE on closed file descriptor %qE",
     779              :                  m_callee_fndecl, m_arg);
     780           14 :     return true;
     781              :   }
     782              : 
     783              : private:
     784              :   diagnostics::paths::event_id_t m_first_close_event;
     785              : };
     786              : 
     787            0 : class fd_use_without_check : public fd_param_diagnostic
     788              : {
     789              : public:
     790            3 :   fd_use_without_check (const fd_state_machine &sm, tree arg,
     791              :                         const tree callee_fndecl, const char *attr_name,
     792              :                         int arg_idx)
     793            3 :       : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
     794              :   {
     795              :   }
     796              : 
     797           64 :   fd_use_without_check (const fd_state_machine &sm, tree arg,
     798              :                         const tree callee_fndecl)
     799           64 :       : fd_param_diagnostic (sm, arg, callee_fndecl)
     800              :   {
     801              :   }
     802              : 
     803              :   const char *
     804          701 :   get_kind () const final override
     805              :   {
     806          701 :     return "fd_use_without_check";
     807              :   }
     808              : 
     809              :   int
     810          107 :   get_controlling_option () const final override
     811              :   {
     812          107 :     return OPT_Wanalyzer_fd_use_without_check;
     813              :   }
     814              : 
     815              :   bool
     816           40 :   emit (diagnostic_emission_context &ctxt) final override
     817              :   {
     818           40 :     bool warned = ctxt.warn ("%qE on possibly invalid file descriptor %qE",
     819              :                              m_callee_fndecl, m_arg);
     820           40 :     if (warned)
     821           40 :      inform_filedescriptor_attribute (DIRS_READ_WRITE);
     822           40 :     return warned;
     823              :   }
     824              : 
     825              :   bool
     826           64 :   describe_state_change (pretty_printer &pp,
     827              :                          const evdesc::state_change &change) override
     828              :   {
     829          128 :     if (m_sm.is_unchecked_fd_p (change.m_new_state))
     830              :       {
     831           56 :         m_first_open_event = change.m_event_id;
     832           56 :         pp_string (&pp, "opened here");
     833           56 :         return true;
     834              :       }
     835              : 
     836            8 :     return fd_diagnostic::describe_state_change (pp, change);
     837              :   }
     838              : 
     839              :   bool
     840           80 :   describe_final_event (pretty_printer &pp,
     841              :                         const evdesc::final_event &) final override
     842              :   {
     843           80 :     if (m_first_open_event.known_p ())
     844           56 :       pp_printf (&pp,
     845              :                  "%qE could be invalid: unchecked value from %@", m_arg,
     846              :                  &m_first_open_event);
     847              :     else
     848           24 :       pp_printf (&pp,
     849              :                  "%qE could be invalid", m_arg);
     850           80 :     return true;
     851              :   }
     852              : 
     853              : private:
     854              :   diagnostics::paths::event_id_t m_first_open_event;
     855              : };
     856              : 
     857              : /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-phase-mismatch.  */
     858              : 
     859            0 : class fd_phase_mismatch : public fd_param_diagnostic
     860              : {
     861              : public:
     862           64 :   fd_phase_mismatch (const fd_state_machine &sm, tree arg,
     863              :                      const tree callee_fndecl,
     864              :                      state_machine::state_t actual_state,
     865              :                      enum expected_phase expected_phase)
     866           64 :   : fd_param_diagnostic (sm, arg, callee_fndecl),
     867           64 :     m_actual_state (actual_state),
     868           64 :     m_expected_phase (expected_phase)
     869              :   {
     870           64 :     gcc_assert (m_sm.is_socket_fd_p (actual_state));
     871           64 :     switch (expected_phase)
     872              :       {
     873            4 :       case EXPECTED_PHASE_CAN_TRANSFER:
     874            4 :         gcc_assert (actual_state == m_sm.m_new_stream_socket
     875              :                     || actual_state == m_sm.m_bound_stream_socket
     876              :                     || actual_state == m_sm.m_listening_stream_socket);
     877              :         break;
     878           16 :       case EXPECTED_PHASE_CAN_BIND:
     879           16 :         gcc_assert (actual_state == m_sm.m_bound_datagram_socket
     880              :                     || actual_state == m_sm.m_bound_stream_socket
     881              :                     || actual_state == m_sm.m_bound_unknown_socket
     882              :                     || actual_state == m_sm.m_connected_stream_socket
     883              :                     || actual_state == m_sm.m_listening_stream_socket);
     884              :         break;
     885           38 :       case EXPECTED_PHASE_CAN_LISTEN:
     886           38 :         gcc_assert (actual_state == m_sm.m_new_stream_socket
     887              :                     || actual_state == m_sm.m_new_unknown_socket
     888              :                     || actual_state == m_sm.m_connected_stream_socket);
     889              :         break;
     890            4 :       case EXPECTED_PHASE_CAN_ACCEPT:
     891            4 :         gcc_assert (actual_state == m_sm.m_new_stream_socket
     892              :                     || actual_state == m_sm.m_new_unknown_socket
     893              :                     || actual_state == m_sm.m_bound_stream_socket
     894              :                     || actual_state == m_sm.m_bound_unknown_socket
     895              :                     || actual_state == m_sm.m_connected_stream_socket);
     896              :         break;
     897            2 :       case EXPECTED_PHASE_CAN_CONNECT:
     898            2 :         gcc_assert (actual_state == m_sm.m_bound_datagram_socket
     899              :                     || actual_state == m_sm.m_bound_stream_socket
     900              :                     || actual_state == m_sm.m_bound_unknown_socket
     901              :                     || actual_state == m_sm.m_listening_stream_socket
     902              :                     || actual_state == m_sm.m_connected_stream_socket);
     903              :         break;
     904              :       }
     905           64 :   }
     906              : 
     907              :   const char *
     908          693 :   get_kind () const final override
     909              :   {
     910          693 :     return "fd_phase_mismatch";
     911              :   }
     912              : 
     913              :   bool
     914           64 :   subclass_equal_p (const pending_diagnostic &base_other) const final override
     915              :   {
     916           64 :     const fd_phase_mismatch &sub_other = (const fd_phase_mismatch &)base_other;
     917           64 :     if (!fd_param_diagnostic ::subclass_equal_p (sub_other))
     918              :       return false;
     919           64 :     return (m_actual_state == sub_other.m_actual_state
     920           64 :             && m_expected_phase == sub_other.m_expected_phase);
     921              :   }
     922              : 
     923              :   int
     924           87 :   get_controlling_option () const final override
     925              :   {
     926           87 :     return OPT_Wanalyzer_fd_phase_mismatch;
     927              :   }
     928              : 
     929              :   bool
     930           23 :   emit (diagnostic_emission_context &ctxt) final override
     931              :   {
     932              :     /* CWE-666: Operation on Resource in Wrong Phase of Lifetime.  */
     933           23 :     ctxt.add_cwe (666);
     934           23 :     return ctxt.warn ("%qE on file descriptor %qE in wrong phase",
     935           23 :                       m_callee_fndecl, m_arg);
     936              :   }
     937              : 
     938              :   bool
     939           46 :   describe_final_event (pretty_printer &pp,
     940              :                         const evdesc::final_event &) final override
     941              :   {
     942           46 :     switch (m_expected_phase)
     943              :       {
     944            6 :       case EXPECTED_PHASE_CAN_TRANSFER:
     945            6 :         {
     946            6 :           if (m_actual_state == m_sm.m_new_stream_socket)
     947              :             {
     948            2 :               pp_printf (&pp,
     949              :                          "%qE expects a stream socket to be connected via %qs"
     950              :                          " but %qE has not yet been bound",
     951              :                          m_callee_fndecl, "accept", m_arg);
     952            2 :               return true;
     953              :             }
     954            4 :           if (m_actual_state == m_sm.m_bound_stream_socket)
     955              :             {
     956            0 :               pp_printf (&pp,
     957              :                          "%qE expects a stream socket to be connected via %qs"
     958              :                          " but %qE is not yet listening",
     959              :                          m_callee_fndecl, "accept", m_arg);
     960            0 :               return true;
     961              :             }
     962            4 :           if (m_actual_state == m_sm.m_listening_stream_socket)
     963              :             {
     964            4 :               pp_printf (&pp,
     965              :                          "%qE expects a stream socket to be connected via"
     966              :                          " the return value of %qs"
     967              :                          " but %qE is listening; wrong file descriptor?",
     968              :                          m_callee_fndecl, "accept", m_arg);
     969            4 :               return true;
     970              :             }
     971              :         }
     972              :         break;
     973            8 :       case EXPECTED_PHASE_CAN_BIND:
     974            8 :         {
     975            8 :           if (m_actual_state == m_sm.m_bound_datagram_socket
     976            8 :               || m_actual_state == m_sm.m_bound_stream_socket
     977            8 :               || m_actual_state == m_sm.m_bound_unknown_socket)
     978              :             {
     979            2 :               pp_printf (&pp,
     980              :                          "%qE expects a new socket file descriptor"
     981              :                          " but %qE has already been bound",
     982              :                          m_callee_fndecl, m_arg);
     983            2 :               return true;
     984              :             }
     985            6 :           if (m_actual_state == m_sm.m_connected_stream_socket)
     986              :             {
     987            2 :               pp_printf (&pp,
     988              :                          "%qE expects a new socket file descriptor"
     989              :                          " but %qE is already connected",
     990              :                          m_callee_fndecl, m_arg);
     991            2 :               return true;
     992              :             }
     993            4 :           if (m_actual_state == m_sm.m_listening_stream_socket)
     994              :             {
     995            4 :               pp_printf (&pp,
     996              :                          "%qE expects a new socket file descriptor"
     997              :                          " but %qE is already listening",
     998              :                          m_callee_fndecl, m_arg);
     999            4 :               return true;
    1000              :             }
    1001              :         }
    1002              :         break;
    1003           26 :       case EXPECTED_PHASE_CAN_LISTEN:
    1004           26 :         {
    1005           26 :           if (m_actual_state == m_sm.m_new_stream_socket
    1006           20 :               || m_actual_state == m_sm.m_new_unknown_socket)
    1007              :             {
    1008           24 :               pp_printf (&pp,
    1009              :                          "%qE expects a bound stream socket file descriptor"
    1010              :                          " but %qE has not yet been bound",
    1011              :                          m_callee_fndecl, m_arg);
    1012           24 :               return true;
    1013              :             }
    1014            2 :           if (m_actual_state == m_sm.m_connected_stream_socket)
    1015              :             {
    1016            2 :               pp_printf (&pp,
    1017              :                          "%qE expects a bound stream socket file descriptor"
    1018              :                          " but %qE is connected",
    1019              :                          m_callee_fndecl, m_arg);
    1020            2 :               return true;
    1021              :             }
    1022              :         }
    1023              :         break;
    1024            4 :       case EXPECTED_PHASE_CAN_ACCEPT:
    1025            4 :         {
    1026            4 :           if (m_actual_state == m_sm.m_new_stream_socket
    1027            2 :               || m_actual_state == m_sm.m_new_unknown_socket)
    1028              :             {
    1029            2 :               pp_printf (&pp,
    1030              :                          "%qE expects a listening stream socket file descriptor"
    1031              :                          " but %qE has not yet been bound",
    1032              :                          m_callee_fndecl, m_arg);
    1033            2 :               return true;
    1034              :             }
    1035            2 :           if (m_actual_state == m_sm.m_bound_stream_socket
    1036            2 :               || m_actual_state == m_sm.m_bound_unknown_socket)
    1037              :             {
    1038            0 :               pp_printf (&pp,
    1039              :                          "%qE expects a listening stream socket file descriptor"
    1040              :                          " whereas %qE is bound but not yet listening",
    1041              :                          m_callee_fndecl, m_arg);
    1042            0 :               return true;
    1043              :             }
    1044            2 :           if (m_actual_state == m_sm.m_connected_stream_socket)
    1045              :             {
    1046            2 :               pp_printf (&pp,
    1047              :                          "%qE expects a listening stream socket file descriptor"
    1048              :                          " but %qE is connected",
    1049              :                          m_callee_fndecl, m_arg);
    1050            2 :               return true;
    1051              :             }
    1052              :         }
    1053              :         break;
    1054            2 :       case EXPECTED_PHASE_CAN_CONNECT:
    1055            2 :         {
    1056            2 :           if (m_actual_state == m_sm.m_bound_datagram_socket
    1057            2 :               || m_actual_state == m_sm.m_bound_stream_socket
    1058            0 :               || m_actual_state == m_sm.m_bound_unknown_socket)
    1059              :             {
    1060            2 :               pp_printf (&pp,
    1061              :                          "%qE expects a new socket file descriptor"
    1062              :                          " but %qE is bound",
    1063              :                          m_callee_fndecl, m_arg);
    1064            2 :               return true;
    1065              :             }
    1066              :           else
    1067              :             {
    1068            0 :               pp_printf (&pp,
    1069              :                          "%qE expects a new socket file descriptor",
    1070              :                          m_callee_fndecl);
    1071            0 :               return true;
    1072              :             }
    1073              :         }
    1074            0 :         break;
    1075              :       }
    1076            0 :     gcc_unreachable ();
    1077              :   }
    1078              : 
    1079              : private:
    1080              :   state_machine::state_t m_actual_state;
    1081              :   enum expected_phase m_expected_phase;
    1082              : };
    1083              : 
    1084              : /* Enum for use by -Wanalyzer-fd-type-mismatch.  */
    1085              : 
    1086              : enum expected_type
    1087              : {
    1088              :  EXPECTED_TYPE_SOCKET,
    1089              :  EXPECTED_TYPE_STREAM_SOCKET
    1090              : };
    1091              : 
    1092              : /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-type-mismatch.  */
    1093              : 
    1094            0 : class fd_type_mismatch : public fd_param_diagnostic
    1095              : {
    1096              : public:
    1097           14 :   fd_type_mismatch (const fd_state_machine &sm, tree arg,
    1098              :                     const tree callee_fndecl,
    1099              :                     state_machine::state_t actual_state,
    1100              :                     enum expected_type expected_type)
    1101           14 :   : fd_param_diagnostic (sm, arg, callee_fndecl),
    1102           14 :     m_actual_state (actual_state),
    1103           14 :     m_expected_type (expected_type)
    1104              :   {
    1105              :   }
    1106              : 
    1107              :   const char *
    1108          146 :   get_kind () const final override
    1109              :   {
    1110          146 :     return "fd_type_mismatch";
    1111              :   }
    1112              : 
    1113              :   bool
    1114           14 :   subclass_equal_p (const pending_diagnostic &base_other) const final override
    1115              :   {
    1116           14 :     const fd_type_mismatch &sub_other = (const fd_type_mismatch &)base_other;
    1117           14 :     if (!fd_param_diagnostic ::subclass_equal_p (sub_other))
    1118              :       return false;
    1119           14 :     return (m_actual_state == sub_other.m_actual_state
    1120           14 :             && m_expected_type == sub_other.m_expected_type);
    1121              :   }
    1122              : 
    1123              :   int
    1124           19 :   get_controlling_option () const final override
    1125              :   {
    1126           19 :     return OPT_Wanalyzer_fd_type_mismatch;
    1127              :   }
    1128              : 
    1129              :   bool
    1130            5 :   emit (diagnostic_emission_context &ctxt) final override
    1131              :   {
    1132            5 :     switch (m_expected_type)
    1133              :       {
    1134            0 :       default:
    1135            0 :         gcc_unreachable ();
    1136            1 :       case EXPECTED_TYPE_SOCKET:
    1137            1 :         return ctxt.warn ("%qE on non-socket file descriptor %qE",
    1138            1 :                           m_callee_fndecl, m_arg);
    1139            4 :       case EXPECTED_TYPE_STREAM_SOCKET:
    1140            8 :         if (m_sm.is_datagram_socket_fd_p (m_actual_state))
    1141            4 :           return ctxt.warn ("%qE on datagram socket file descriptor %qE",
    1142            4 :                             m_callee_fndecl, m_arg);
    1143              :         else
    1144            0 :           return ctxt.warn ("%qE on non-stream-socket file descriptor %qE",
    1145            0 :                             m_callee_fndecl, m_arg);
    1146              :       }
    1147              :   }
    1148              : 
    1149              :   bool
    1150           10 :   describe_final_event (pretty_printer &pp,
    1151              :                         const evdesc::final_event &) final override
    1152              :   {
    1153           10 :     switch (m_expected_type)
    1154              :       {
    1155              :       default:
    1156              :         break;
    1157              :         gcc_unreachable ();
    1158           10 :       case EXPECTED_TYPE_SOCKET:
    1159           10 :       case EXPECTED_TYPE_STREAM_SOCKET:
    1160           10 :         if (!m_sm.is_socket_fd_p (m_actual_state))
    1161              :           {
    1162            2 :             pp_printf (&pp,
    1163              :                        "%qE expects a socket file descriptor"
    1164              :                        " but %qE is not a socket",
    1165              :                        m_callee_fndecl, m_arg);
    1166            2 :             return true;
    1167              :           }
    1168              :       }
    1169            8 :     gcc_assert (m_expected_type == EXPECTED_TYPE_STREAM_SOCKET);
    1170            0 :     gcc_assert (m_sm.is_datagram_socket_fd_p (m_actual_state));
    1171            8 :     pp_printf (&pp,
    1172              :                "%qE expects a stream socket file descriptor"
    1173              :                " but %qE is a datagram socket",
    1174              :                m_callee_fndecl, m_arg);
    1175            8 :     return true;
    1176              :   }
    1177              : 
    1178              : private:
    1179              :   state_machine::state_t m_actual_state;
    1180              :   enum expected_type m_expected_type;
    1181              : };
    1182              : 
    1183         3377 : fd_state_machine::fd_state_machine (logger *logger)
    1184              :     : state_machine ("file-descriptor", logger),
    1185         6754 :       m_constant_fd (add_state ("fd-constant")),
    1186         3377 :       m_unchecked_read_write (add_state ("fd-unchecked-read-write")),
    1187         3377 :       m_unchecked_read_only (add_state ("fd-unchecked-read-only")),
    1188         3377 :       m_unchecked_write_only (add_state ("fd-unchecked-write-only")),
    1189         3377 :       m_valid_read_write (add_state ("fd-valid-read-write")),
    1190         3377 :       m_valid_read_only (add_state ("fd-valid-read-only")),
    1191         3377 :       m_valid_write_only (add_state ("fd-valid-write-only")),
    1192         3377 :       m_invalid (add_state ("fd-invalid")),
    1193         3377 :       m_closed (add_state ("fd-closed")),
    1194         3377 :       m_new_datagram_socket (add_state ("fd-new-datagram-socket")),
    1195         3377 :       m_new_stream_socket (add_state ("fd-new-stream-socket")),
    1196         3377 :       m_new_unknown_socket (add_state ("fd-new-unknown-socket")),
    1197         3377 :       m_bound_datagram_socket (add_state ("fd-bound-datagram-socket")),
    1198         3377 :       m_bound_stream_socket (add_state ("fd-bound-stream-socket")),
    1199         3377 :       m_bound_unknown_socket (add_state ("fd-bound-unknown-socket")),
    1200         3377 :       m_listening_stream_socket (add_state ("fd-listening-stream-socket")),
    1201         3377 :       m_connected_stream_socket (add_state ("fd-connected-stream-socket")),
    1202         3377 :       m_stop (add_state ("fd-stop")),
    1203         3377 :       m_O_ACCMODE (get_stashed_constant_by_name ("O_ACCMODE")),
    1204         3377 :       m_O_RDONLY (get_stashed_constant_by_name ("O_RDONLY")),
    1205         3377 :       m_O_WRONLY (get_stashed_constant_by_name ("O_WRONLY")),
    1206         3377 :       m_SOCK_STREAM (get_stashed_constant_by_name ("SOCK_STREAM")),
    1207         6754 :       m_SOCK_DGRAM (get_stashed_constant_by_name ("SOCK_DGRAM"))
    1208              : {
    1209         3377 : }
    1210              : 
    1211              : bool
    1212      1422960 : fd_state_machine::is_unchecked_fd_p (state_t s) const
    1213              : {
    1214      1422960 :   return (s == m_unchecked_read_write
    1215      1422667 :        || s == m_unchecked_read_only
    1216      1423159 :        || s == m_unchecked_write_only);
    1217              : }
    1218              : 
    1219              : bool
    1220      1422611 : fd_state_machine::is_valid_fd_p (state_t s) const
    1221              : {
    1222      1422611 :   return (s == m_valid_read_write
    1223      1422497 :        || s == m_valid_read_only
    1224      1422579 :        || s == m_valid_write_only);
    1225              : }
    1226              : 
    1227              : bool
    1228      1421950 : fd_state_machine::is_socket_fd_p (state_t s) const
    1229              : {
    1230      1421950 :   return (s == m_new_datagram_socket
    1231      1421880 :           || s == m_new_stream_socket
    1232      1421823 :           || s == m_new_unknown_socket
    1233      1421632 :           || s == m_bound_datagram_socket
    1234      1421593 :           || s == m_bound_stream_socket
    1235      1421556 :           || s == m_bound_unknown_socket
    1236      1421418 :           || s == m_listening_stream_socket
    1237      2843263 :           || s == m_connected_stream_socket);
    1238              : }
    1239              : 
    1240              : bool
    1241           12 : fd_state_machine::is_datagram_socket_fd_p (state_t s) const
    1242              : {
    1243           12 :   return (s == m_new_datagram_socket
    1244            3 :           || s == m_new_unknown_socket
    1245            3 :           || s == m_bound_datagram_socket
    1246           12 :           || s == m_bound_unknown_socket);
    1247              : }
    1248              : 
    1249              : bool
    1250           52 : fd_state_machine::is_stream_socket_fd_p (state_t s) const
    1251              : {
    1252           52 :   return (s == m_new_stream_socket
    1253           42 :           || s == m_new_unknown_socket
    1254           14 :           || s == m_bound_stream_socket
    1255           14 :           || s == m_bound_unknown_socket
    1256           14 :           || s == m_listening_stream_socket
    1257           66 :           || s == m_connected_stream_socket);
    1258              : }
    1259              : 
    1260              : enum access_mode
    1261           88 : fd_state_machine::get_access_mode_from_flag (int flag) const
    1262              : {
    1263           88 :   if (m_O_ACCMODE && TREE_CODE (m_O_ACCMODE) == INTEGER_CST)
    1264              :     {
    1265           37 :       const unsigned HOST_WIDE_INT mask_val = TREE_INT_CST_LOW (m_O_ACCMODE);
    1266           37 :       const unsigned HOST_WIDE_INT masked_flag = flag & mask_val;
    1267              : 
    1268           37 :       if (m_O_RDONLY && TREE_CODE (m_O_RDONLY) == INTEGER_CST)
    1269           37 :         if (masked_flag == TREE_INT_CST_LOW (m_O_RDONLY))
    1270              :           return READ_ONLY;
    1271              : 
    1272           31 :       if (m_O_WRONLY && TREE_CODE (m_O_WRONLY) == INTEGER_CST)
    1273           31 :         if (masked_flag == TREE_INT_CST_LOW (m_O_WRONLY))
    1274            6 :           return WRITE_ONLY;
    1275              :     }
    1276              :   return READ_WRITE;
    1277              : }
    1278              : 
    1279              : bool
    1280           64 : fd_state_machine::is_readonly_fd_p (state_t state) const
    1281              : {
    1282           64 :   return (state == m_unchecked_read_only || state == m_valid_read_only);
    1283              : }
    1284              : 
    1285              : bool
    1286           83 : fd_state_machine::is_writeonly_fd_p (state_t state) const
    1287              : {
    1288           83 :   return (state == m_unchecked_write_only || state == m_valid_write_only);
    1289              : }
    1290              : 
    1291              : bool
    1292         1098 : fd_state_machine::is_closed_fd_p (state_t state) const
    1293              : {
    1294         1098 :   return (state == m_closed);
    1295              : }
    1296              : 
    1297              : bool
    1298           83 : fd_state_machine::is_constant_fd_p (state_t state) const
    1299              : {
    1300           83 :   return (state == m_constant_fd);
    1301              : }
    1302              : 
    1303              : fd_state_machine::state_t
    1304            9 : fd_state_machine::valid_to_unchecked_state (state_t state) const
    1305              : {
    1306            9 :   if (state == m_valid_read_write)
    1307            6 :     return m_unchecked_read_write;
    1308            3 :   else if (state == m_valid_write_only)
    1309            2 :     return m_unchecked_write_only;
    1310            1 :   else if (state == m_valid_read_only)
    1311            1 :     return m_unchecked_read_only;
    1312              :   else
    1313            0 :     gcc_unreachable ();
    1314              :   return nullptr;
    1315              : }
    1316              : 
    1317              : void
    1318           18 : fd_state_machine::mark_as_valid_fd (region_model *model,
    1319              :                                     sm_state_map *smap,
    1320              :                                     const svalue *fd_sval,
    1321              :                                     const extrinsic_state &ext_state) const
    1322              : {
    1323            0 :   smap->set_state (model, fd_sval, m_valid_read_write, nullptr, ext_state);
    1324            0 : }
    1325              : 
    1326              : bool
    1327       263888 : fd_state_machine::on_stmt (sm_context &sm_ctxt,
    1328              :                            const gimple *stmt) const
    1329              : {
    1330       263888 :   if (const gcall *call = dyn_cast<const gcall *> (stmt))
    1331        49635 :     if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call))
    1332              :       {
    1333        45837 :         if (is_named_call_p (callee_fndecl, "open", *call, 2))
    1334              :           {
    1335           96 :             on_open (sm_ctxt, *call);
    1336           96 :             return true;
    1337              :           } //  "open"
    1338              : 
    1339        45741 :         if (is_named_call_p (callee_fndecl, "creat", *call, 2))
    1340              :           {
    1341           14 :             on_creat (sm_ctxt, *call);
    1342           14 :             return true;
    1343              :           } // "creat"
    1344              : 
    1345        45727 :         if (is_named_call_p (callee_fndecl, "close", *call, 1))
    1346              :           {
    1347          395 :             on_close (sm_ctxt, *call);
    1348          395 :             return true;
    1349              :           } //  "close"
    1350              : 
    1351        45332 :         if (is_named_call_p (callee_fndecl, "write", *call, 3))
    1352              :           {
    1353           66 :             on_write (sm_ctxt, *call, callee_fndecl);
    1354           66 :             return true;
    1355              :           } // "write"
    1356              : 
    1357        45266 :         if (is_named_call_p (callee_fndecl, "read", *call, 3))
    1358              :           {
    1359           85 :             on_read (sm_ctxt, *call, callee_fndecl);
    1360           85 :             return true;
    1361              :           } // "read"
    1362              : 
    1363        45181 :         if (is_named_call_p (callee_fndecl, "dup", *call, 1))
    1364              :           {
    1365           15 :             check_for_dup (sm_ctxt, *call, callee_fndecl, DUP_1);
    1366           15 :             return true;
    1367              :           }
    1368              : 
    1369        45166 :         if (is_named_call_p (callee_fndecl, "dup2", *call, 2))
    1370              :           {
    1371            8 :             check_for_dup (sm_ctxt, *call, callee_fndecl, DUP_2);
    1372            8 :             return true;
    1373              :           }
    1374              : 
    1375        45158 :         if (is_named_call_p (callee_fndecl, "dup3", *call, 3))
    1376              :           {
    1377            3 :             check_for_dup (sm_ctxt, *call, callee_fndecl, DUP_3);
    1378            3 :             return true;
    1379              :           }
    1380              : 
    1381        45155 :         {
    1382              :           // Handle __attribute__((fd_arg))
    1383              : 
    1384        45155 :           check_for_fd_attrs (sm_ctxt, *call, callee_fndecl,
    1385              :                               "fd_arg", DIRS_READ_WRITE);
    1386              : 
    1387              :           // Handle __attribute__((fd_arg_read))
    1388              : 
    1389        45155 :           check_for_fd_attrs (sm_ctxt, *call, callee_fndecl,
    1390              :                               "fd_arg_read", DIRS_READ);
    1391              : 
    1392              :           // Handle __attribute__((fd_arg_write))
    1393              : 
    1394        45155 :           check_for_fd_attrs (sm_ctxt, *call, callee_fndecl,
    1395              :                               "fd_arg_write", DIRS_WRITE);
    1396              :         }
    1397              :       }
    1398              : 
    1399              :   return false;
    1400              : }
    1401              : 
    1402              : void
    1403       135465 : fd_state_machine::check_for_fd_attrs (
    1404              :     sm_context &sm_ctxt,
    1405              :     const gcall &call, const tree callee_fndecl, const char *attr_name,
    1406              :     access_directions fd_attr_access_dir) const
    1407              : {
    1408              :   /* Handle interesting fd attributes of the callee_fndecl,
    1409              :      or prioritize those of the builtin that callee_fndecl is
    1410              :      expected to be.
    1411              :      Might want this to be controlled by a flag.  */
    1412       135465 :   tree fndecl = callee_fndecl;
    1413              :   /* If call is recognized as a builtin known_function,
    1414              :      use that builtin's function_decl.  */
    1415       135465 :   if (const region_model *old_model = sm_ctxt.get_old_region_model ())
    1416       270930 :     if (const builtin_known_function *builtin_kf
    1417       135465 :          = old_model->get_builtin_kf (call))
    1418        60750 :       fndecl = builtin_kf->builtin_decl ();
    1419              : 
    1420       135465 :   tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
    1421       135465 :   attrs = lookup_attribute (attr_name, attrs);
    1422       135465 :   if (!attrs)
    1423       135453 :     return;
    1424              : 
    1425           12 :   if (!TREE_VALUE (attrs))
    1426              :     return;
    1427              : 
    1428           12 :   auto_bitmap argmap;
    1429              : 
    1430           24 :   for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))
    1431              :     {
    1432           12 :       unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;
    1433           12 :       bitmap_set_bit (argmap, val);
    1434              :     }
    1435           12 :   if (bitmap_empty_p (argmap))
    1436            0 :     return;
    1437              : 
    1438           24 :   for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (&call); arg_idx++)
    1439              :     {
    1440           12 :       tree arg = gimple_call_arg (&call, arg_idx);
    1441           12 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    1442           12 :       state_t state = sm_ctxt.get_state (arg);
    1443           12 :       bool bit_set = bitmap_bit_p (argmap, arg_idx);
    1444           12 :       if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE)
    1445            6 :         continue;
    1446           12 :       if (bit_set) // Check if arg_idx is marked by any of the file descriptor
    1447              :                    // attributes
    1448              :         {
    1449              : 
    1450              :           /* Do use the fndecl that caused the warning so that the
    1451              :              misused attributes are printed and the user not confused.  */
    1452           12 :           if (is_closed_fd_p (state))
    1453              :             {
    1454              : 
    1455            3 :               sm_ctxt.warn (arg,
    1456              :                             std::make_unique<fd_use_after_close>
    1457            3 :                             (*this, diag_arg,
    1458              :                              fndecl, attr_name,
    1459              :                              arg_idx));
    1460            3 :               continue;
    1461              :             }
    1462              : 
    1463           12 :           if (!(is_valid_fd_p (state) || (state == m_stop)))
    1464              :             {
    1465            3 :               if (!is_constant_fd_p (state))
    1466              :                 {
    1467            3 :                   sm_ctxt.warn (arg,
    1468              :                                 std::make_unique<fd_use_without_check>
    1469            3 :                                 (*this, diag_arg,
    1470              :                                  fndecl, attr_name,
    1471              :                                  arg_idx));
    1472            3 :                   continue;
    1473              :                 }
    1474              :             }
    1475              : 
    1476            6 :           switch (fd_attr_access_dir)
    1477              :             {
    1478              :             case DIRS_READ_WRITE:
    1479              :               break;
    1480            3 :             case DIRS_READ:
    1481              : 
    1482            3 :               if (is_writeonly_fd_p (state))
    1483              :                 {
    1484            3 :                   sm_ctxt.warn
    1485            3 :                     (arg,
    1486            3 :                      std::make_unique<fd_access_mode_mismatch> (*this, diag_arg,
    1487            6 :                                                                 DIRS_WRITE,
    1488              :                                                                 fndecl,
    1489              :                                                                 attr_name,
    1490              :                                                                 arg_idx));
    1491              :                 }
    1492              : 
    1493              :               break;
    1494            3 :             case DIRS_WRITE:
    1495              : 
    1496            9 :               if (is_readonly_fd_p (state))
    1497              :                 {
    1498            3 :                   sm_ctxt.warn
    1499            3 :                     (arg,
    1500            3 :                      std::make_unique<fd_access_mode_mismatch> (*this, diag_arg,
    1501            6 :                                                                 DIRS_READ,
    1502              :                                                                 fndecl,
    1503              :                                                                 attr_name,
    1504              :                                                                 arg_idx));
    1505              :                 }
    1506              : 
    1507              :               break;
    1508              :             }
    1509              :         }
    1510              :     }
    1511           12 : }
    1512              : 
    1513              : 
    1514              : void
    1515           96 : fd_state_machine::on_open (sm_context &sm_ctxt, const gcall &call) const
    1516              : {
    1517           96 :   tree lhs = gimple_call_lhs (&call);
    1518           96 :   if (lhs)
    1519              :     {
    1520           92 :       tree arg = gimple_call_arg (&call, 1);
    1521           92 :       enum access_mode mode = READ_WRITE;
    1522           92 :       if (TREE_CODE (arg) == INTEGER_CST)
    1523              :         {
    1524           88 :           int flag = TREE_INT_CST_LOW (arg);
    1525           88 :           mode = get_access_mode_from_flag (flag);
    1526              :         }
    1527           88 :       switch (mode)
    1528              :         {
    1529            6 :         case READ_ONLY:
    1530            6 :           sm_ctxt.on_transition (lhs, m_start,
    1531            6 :                                  m_unchecked_read_only);
    1532            6 :           break;
    1533            6 :         case WRITE_ONLY:
    1534            6 :           sm_ctxt.on_transition (lhs, m_start,
    1535            6 :                                  m_unchecked_write_only);
    1536            6 :           break;
    1537           80 :         default:
    1538           80 :           sm_ctxt.on_transition (lhs, m_start,
    1539           80 :                                  m_unchecked_read_write);
    1540              :         }
    1541              :     }
    1542              :   else
    1543              :     {
    1544            4 :       sm_ctxt.warn (NULL_TREE,
    1545            4 :                     std::make_unique<fd_leak> (*this, NULL_TREE, nullptr));
    1546              :     }
    1547           96 : }
    1548              : 
    1549              : void
    1550           14 : fd_state_machine::on_creat (sm_context &sm_ctxt, const gcall &call) const
    1551              : {
    1552           14 :   tree lhs = gimple_call_lhs (&call);
    1553           14 :   if (lhs)
    1554           10 :     sm_ctxt.on_transition (lhs, m_start, m_unchecked_write_only);
    1555              :   else
    1556            4 :     sm_ctxt.warn (NULL_TREE,
    1557            4 :                   std::make_unique<fd_leak> (*this, NULL_TREE, nullptr));
    1558           14 : }
    1559              : 
    1560              : void
    1561           26 : fd_state_machine::check_for_dup (sm_context &sm_ctxt, const gcall &call,
    1562              :                                  const tree callee_fndecl, enum dup kind) const
    1563              : {
    1564           26 :   tree lhs = gimple_call_lhs (&call);
    1565           26 :   tree arg_1 = gimple_call_arg (&call, 0);
    1566           26 :   state_t state_arg_1 = sm_ctxt.get_state (arg_1);
    1567           26 :   if (state_arg_1 == m_stop)
    1568              :     return;
    1569           26 :   if (!(is_constant_fd_p (state_arg_1) || is_valid_fd_p (state_arg_1)
    1570           13 :         || state_arg_1 == m_start))
    1571              :     {
    1572            6 :       check_for_open_fd (sm_ctxt, call, callee_fndecl, DIRS_READ_WRITE);
    1573            6 :       return;
    1574              :     }
    1575           20 :   switch (kind)
    1576              :     {
    1577           13 :     case DUP_1:
    1578           13 :       if (lhs)
    1579              :         {
    1580           13 :           if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
    1581            7 :             sm_ctxt.set_next_state (lhs, m_unchecked_read_write);
    1582              :           else
    1583            6 :             sm_ctxt.set_next_state (lhs,
    1584              :                                     valid_to_unchecked_state (state_arg_1));
    1585              :         }
    1586           18 :       break;
    1587              : 
    1588            7 :     case DUP_2:
    1589            7 :     case DUP_3:
    1590            7 :       tree arg_2 = gimple_call_arg (&call, 1);
    1591            7 :       state_t state_arg_2 = sm_ctxt.get_state (arg_2);
    1592            7 :       tree diag_arg_2 = sm_ctxt.get_diagnostic_tree (arg_2);
    1593            7 :       if (state_arg_2 == m_stop)
    1594            2 :         return;
    1595              :       /* Check if -1 was passed as second argument to dup2.  */
    1596            7 :       if (!(is_constant_fd_p (state_arg_2) || is_valid_fd_p (state_arg_2)
    1597            2 :             || state_arg_2 == m_start))
    1598              :         {
    1599            2 :           sm_ctxt.warn
    1600            2 :             (arg_2,
    1601            2 :              std::make_unique<fd_use_without_check> (*this, diag_arg_2,
    1602              :                                                      callee_fndecl));
    1603            2 :           return;
    1604              :         }
    1605              :       /* dup2 returns value of its second argument on success.But, the
    1606              :       access mode of the returned file descriptor depends on the duplicated
    1607              :       file descriptor i.e the first argument.  */
    1608            5 :       if (lhs)
    1609              :         {
    1610            5 :           if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
    1611            2 :             sm_ctxt.set_next_state (lhs, m_unchecked_read_write);
    1612              :           else
    1613            3 :             sm_ctxt.set_next_state (lhs,
    1614              :                                     valid_to_unchecked_state (state_arg_1));
    1615              :         }
    1616              : 
    1617              :       break;
    1618              :     }
    1619              : }
    1620              : 
    1621              : void
    1622          395 : fd_state_machine::on_close (sm_context &sm_ctxt, const gcall &call) const
    1623              : {
    1624          395 :   tree arg = gimple_call_arg (&call, 0);
    1625          395 :   state_t state = sm_ctxt.get_state (arg);
    1626          395 :   tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    1627              : 
    1628          395 :   sm_ctxt.on_transition (arg, m_start, m_closed);
    1629          395 :   sm_ctxt.on_transition (arg, m_unchecked_read_write, m_closed);
    1630          395 :   sm_ctxt.on_transition (arg, m_unchecked_read_only, m_closed);
    1631          395 :   sm_ctxt.on_transition (arg, m_unchecked_write_only, m_closed);
    1632          395 :   sm_ctxt.on_transition (arg, m_valid_read_write, m_closed);
    1633          395 :   sm_ctxt.on_transition (arg, m_valid_read_only, m_closed);
    1634          395 :   sm_ctxt.on_transition (arg, m_valid_write_only, m_closed);
    1635          395 :   sm_ctxt.on_transition (arg, m_constant_fd, m_closed);
    1636          395 :   sm_ctxt.on_transition (arg, m_new_datagram_socket, m_closed);
    1637          395 :   sm_ctxt.on_transition (arg, m_new_stream_socket, m_closed);
    1638          395 :   sm_ctxt.on_transition (arg, m_new_unknown_socket, m_closed);
    1639          395 :   sm_ctxt.on_transition (arg, m_bound_datagram_socket, m_closed);
    1640          395 :   sm_ctxt.on_transition (arg, m_bound_stream_socket, m_closed);
    1641          395 :   sm_ctxt.on_transition (arg, m_bound_unknown_socket, m_closed);
    1642          395 :   sm_ctxt.on_transition (arg, m_listening_stream_socket, m_closed);
    1643          395 :   sm_ctxt.on_transition (arg, m_connected_stream_socket, m_closed);
    1644              : 
    1645          395 :   if (is_closed_fd_p (state))
    1646              :     {
    1647           28 :       sm_ctxt.warn (arg,
    1648           28 :                     std::make_unique<fd_double_close> (*this, diag_arg));
    1649           28 :       sm_ctxt.set_next_state (arg, m_stop);
    1650              :     }
    1651          395 : }
    1652              : void
    1653           85 : fd_state_machine::on_read (sm_context &sm_ctxt, const gcall &call,
    1654              :                            const tree callee_fndecl) const
    1655              : {
    1656           85 :   check_for_open_fd (sm_ctxt,call, callee_fndecl, DIRS_READ);
    1657            0 : }
    1658              : void
    1659           66 : fd_state_machine::on_write (sm_context &sm_ctxt, const gcall &call,
    1660              :                             const tree callee_fndecl) const
    1661              : {
    1662           66 :   check_for_open_fd (sm_ctxt,call, callee_fndecl, DIRS_WRITE);
    1663            0 : }
    1664              : 
    1665              : void
    1666          157 : fd_state_machine::check_for_open_fd (
    1667              :     sm_context &sm_ctxt,
    1668              :     const gcall &call, const tree callee_fndecl,
    1669              :     enum access_directions callee_fndecl_dir) const
    1670              : {
    1671          157 :   tree arg = gimple_call_arg (&call, 0);
    1672          157 :   tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    1673          157 :   state_t state = sm_ctxt.get_state (arg);
    1674              : 
    1675          157 :   if (is_closed_fd_p (state))
    1676              :     {
    1677           12 :       sm_ctxt.warn (arg,
    1678           12 :                     std::make_unique<fd_use_after_close> (*this, diag_arg,
    1679              :                                                           callee_fndecl));
    1680              :     }
    1681              : 
    1682              :   else
    1683              :     {
    1684          145 :       if (state == m_new_stream_socket
    1685          144 :           || state == m_bound_stream_socket
    1686          144 :           || state == m_listening_stream_socket)
    1687              :         /* Complain about fncall on socket in wrong phase.  */
    1688            4 :         sm_ctxt.warn
    1689            4 :           (arg,
    1690            4 :            std::make_unique<fd_phase_mismatch> (*this, diag_arg,
    1691              :                                                 callee_fndecl,
    1692              :                                                 state,
    1693            8 :                                                 EXPECTED_PHASE_CAN_TRANSFER));
    1694          141 :       else if (!(is_valid_fd_p (state)
    1695          118 :                  || state == m_new_datagram_socket
    1696          118 :                  || state == m_bound_unknown_socket
    1697          116 :                  || state == m_connected_stream_socket
    1698           93 :                  || state == m_start
    1699           70 :                  || state == m_stop))
    1700              :         {
    1701           47 :           if (!is_constant_fd_p (state))
    1702           36 :             sm_ctxt.warn
    1703           36 :               (arg,
    1704           36 :                std::make_unique<fd_use_without_check> (*this, diag_arg,
    1705              :                                                        callee_fndecl));
    1706              :         }
    1707          145 :       switch (callee_fndecl_dir)
    1708              :         {
    1709              :         case DIRS_READ_WRITE:
    1710              :           break;
    1711           80 :         case DIRS_READ:
    1712           80 :           if (is_writeonly_fd_p (state))
    1713              :             {
    1714            4 :               tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    1715            4 :               sm_ctxt.warn (arg,
    1716              :                             std::make_unique<fd_access_mode_mismatch>
    1717            4 :                               (*this, diag_arg, DIRS_WRITE, callee_fndecl));
    1718              :             }
    1719              : 
    1720              :           break;
    1721           61 :         case DIRS_WRITE:
    1722              : 
    1723           61 :           if (is_readonly_fd_p (state))
    1724              :             {
    1725            2 :               tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    1726            2 :               sm_ctxt.warn (arg,
    1727              :                             std::make_unique<fd_access_mode_mismatch>
    1728            2 :                               (*this, diag_arg, DIRS_READ, callee_fndecl));
    1729              :             }
    1730              :           break;
    1731              :         }
    1732              :     }
    1733          157 : }
    1734              : 
    1735              : static bool
    1736          327 : add_constraint_ge_zero (region_model *model,
    1737              :                         const svalue *fd_sval,
    1738              :                         region_model_context *ctxt)
    1739              : {
    1740          327 :   const svalue *zero
    1741          327 :     = model->get_manager ()->get_or_create_int_cst (integer_type_node, 0);
    1742          327 :   return model->add_constraint (fd_sval, GE_EXPR, zero, ctxt);
    1743              : }
    1744              : 
    1745              : /* Get the state for a new socket type based on SOCKET_TYPE_SVAL,
    1746              :    a SOCK_* value.  */
    1747              : 
    1748              : state_machine::state_t
    1749          101 : fd_state_machine::
    1750              : get_state_for_socket_type (const svalue *socket_type_sval) const
    1751              : {
    1752          101 :   if (tree socket_type_cst = socket_type_sval->maybe_get_constant ())
    1753              :     {
    1754              :       /* Attempt to use SOCK_* constants stashed from the frontend.  */
    1755           45 :       if (tree_int_cst_equal (socket_type_cst, m_SOCK_STREAM))
    1756           20 :         return m_new_stream_socket;
    1757           25 :       if (tree_int_cst_equal (socket_type_cst, m_SOCK_DGRAM))
    1758           16 :         return m_new_datagram_socket;
    1759              :     }
    1760              : 
    1761              :   /* Unrecognized constant, or a symbolic "type" value.  */
    1762           65 :   return m_new_unknown_socket;
    1763              : }
    1764              : 
    1765              : /* Update the model and fd state for an outcome of a call to "socket",
    1766              :    where SUCCESSFUL indicate which of the two outcomes.
    1767              :    Return true if the outcome is feasible, or false to reject it.  */
    1768              : 
    1769              : bool
    1770          214 : fd_state_machine::on_socket (const call_details &cd,
    1771              :                              bool successful,
    1772              :                              sm_context &sm_ctxt,
    1773              :                              const extrinsic_state &) const
    1774              : {
    1775          214 :   const gcall &call = cd.get_call_stmt ();
    1776          214 :   region_model *model = cd.get_model ();
    1777              : 
    1778          214 :   if (successful)
    1779              :     {
    1780          107 :       if (gimple_call_lhs (&call))
    1781              :         {
    1782          101 :           conjured_purge p (model, cd.get_ctxt ());
    1783          101 :           region_model_manager *mgr = model->get_manager ();
    1784          101 :           const svalue *new_fd
    1785          101 :             = mgr->get_or_create_conjured_svalue (integer_type_node,
    1786              :                                                   &call,
    1787              :                                                   cd.get_lhs_region (),
    1788              :                                                   p);
    1789          101 :           if (!add_constraint_ge_zero (model, new_fd, cd.get_ctxt ()))
    1790            0 :             return false;
    1791              : 
    1792          101 :           const svalue *socket_type_sval = cd.get_arg_svalue (1);
    1793          101 :           state_machine::state_t new_state
    1794          101 :             = get_state_for_socket_type (socket_type_sval);
    1795          101 :           sm_ctxt.on_transition (new_fd, m_start, new_state);
    1796          101 :           model->set_value (cd.get_lhs_region (), new_fd, cd.get_ctxt ());
    1797              :         }
    1798              :       else
    1799            6 :         sm_ctxt.warn (NULL_TREE,
    1800            6 :                       std::make_unique<fd_leak> (*this, NULL_TREE, nullptr));
    1801              :     }
    1802              :   else
    1803              :     {
    1804              :       /* Return -1; set errno.  */
    1805          107 :       model->update_for_int_cst_return (cd, -1, true);
    1806          107 :       model->set_errno (cd);
    1807              :     }
    1808              : 
    1809              :   return true;
    1810              : }
    1811              : 
    1812              : /* Check that FD_SVAL is usable by socket APIs.
    1813              :    Complain if it has been closed, if it is a non-socket,
    1814              :    or is invalid.
    1815              :    If COMPLAINED is non-NULL and a problem is found,
    1816              :    write *COMPLAINED = true.
    1817              : 
    1818              :    If SUCCESSFUL is true, attempt to add the constraint that FD_SVAL >= 0.
    1819              :    Return true if this outcome is feasible.  */
    1820              : 
    1821              : bool
    1822          434 : fd_state_machine::check_for_socket_fd (const call_details &cd,
    1823              :                                        bool successful,
    1824              :                                        sm_context &sm_ctxt,
    1825              :                                        const svalue *fd_sval,
    1826              :                                        state_t old_state,
    1827              :                                        bool *complained) const
    1828              : {
    1829          434 :   if (is_closed_fd_p (old_state))
    1830              :     {
    1831            0 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval);
    1832            0 :       sm_ctxt.warn
    1833            0 :         (fd_sval,
    1834            0 :          std::make_unique<fd_use_after_close> (*this, diag_arg,
    1835            0 :                                                cd.get_fndecl_for_call ()));
    1836            0 :       if (complained)
    1837            0 :         *complained = true;
    1838            0 :       if (successful)
    1839            0 :         return false;
    1840              :     }
    1841          868 :   else if (is_unchecked_fd_p (old_state) || is_valid_fd_p (old_state))
    1842              :     {
    1843              :       /* Complain about non-socket.  */
    1844            4 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval);
    1845            4 :       sm_ctxt.warn
    1846            4 :         (fd_sval,
    1847            4 :          std::make_unique<fd_type_mismatch> (*this, diag_arg,
    1848            4 :                                              cd.get_fndecl_for_call (),
    1849              :                                              old_state,
    1850            4 :                                              EXPECTED_TYPE_SOCKET));
    1851            4 :       if (complained)
    1852            4 :         *complained = true;
    1853            4 :       if (successful)
    1854            2 :         return false;
    1855              :     }
    1856          430 :   else if (old_state == m_invalid)
    1857              :     {
    1858           26 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval);
    1859           26 :       sm_ctxt.warn
    1860           26 :         (fd_sval,
    1861           26 :          std::make_unique<fd_use_without_check> (*this, diag_arg,
    1862           26 :                                                  cd.get_fndecl_for_call ()));
    1863           26 :       if (complained)
    1864           24 :         *complained = true;
    1865           26 :       if (successful)
    1866           13 :         return false;
    1867              :     }
    1868              : 
    1869          419 :   if (successful)
    1870          202 :     if (!add_constraint_ge_zero (cd.get_model (), fd_sval, cd.get_ctxt ()))
    1871              :       return false;
    1872              : 
    1873              :   return true;
    1874              : }
    1875              : 
    1876              : /* For use by "bind" and "connect".
    1877              :    As per fd_state_machine::check_for_socket_fd above,
    1878              :    but also complain if we don't have a new socket, and check that
    1879              :    we can read up to the size bytes from the address.  */
    1880              : 
    1881              : bool
    1882          270 : fd_state_machine::check_for_new_socket_fd (const call_details &cd,
    1883              :                                            bool successful,
    1884              :                                            sm_context &sm_ctxt,
    1885              :                                            const svalue *fd_sval,
    1886              :                                            state_t old_state,
    1887              :                                            enum expected_phase expected_phase)
    1888              :   const
    1889              : {
    1890          270 :   bool complained = false;
    1891              : 
    1892              :   /* Check address and len.  */
    1893          270 :   const svalue *address_sval = cd.get_arg_svalue (1);
    1894          270 :   const svalue *len_sval = cd.get_arg_svalue (2);
    1895              : 
    1896              :   /* Check that we can read the given number of bytes from the
    1897              :      address.  */
    1898          270 :   region_model *model = cd.get_model ();
    1899          270 :   const region *address_reg
    1900          270 :     = model->deref_rvalue (address_sval, cd.get_arg_tree (1),
    1901              :                            cd.get_ctxt ());
    1902          270 :   const region *sized_address_reg
    1903          270 :     = model->get_manager ()->get_sized_region (address_reg,
    1904              :                                                NULL_TREE,
    1905              :                                                len_sval);
    1906          270 :   model->get_store_value (sized_address_reg, cd.get_ctxt ());
    1907              : 
    1908          270 :   if (!check_for_socket_fd (cd, successful, sm_ctxt,
    1909              :                             fd_sval, old_state, &complained))
    1910              :     return false;
    1911          256 :   else if (!complained
    1912          242 :            && !(old_state == m_new_stream_socket
    1913          210 :                 || old_state == m_new_datagram_socket
    1914          182 :                 || old_state == m_new_unknown_socket
    1915           92 :                 || old_state == m_start
    1916           50 :                 || old_state == m_stop
    1917           46 :                 || old_state == m_constant_fd))
    1918              :     {
    1919              :       /* Complain about "bind" or "connect" in wrong phase.  */
    1920           18 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval);
    1921           18 :       sm_ctxt.warn
    1922           18 :         (fd_sval,
    1923           18 :          std::make_unique<fd_phase_mismatch> (*this, diag_arg,
    1924           18 :                                               cd.get_fndecl_for_call (),
    1925              :                                               old_state,
    1926              :                                               expected_phase));
    1927           18 :       if (successful)
    1928            9 :         return false;
    1929              :     }
    1930          238 :   else if (!successful)
    1931              :     {
    1932              :       /* If we were in the start state, assume we had a new socket.  */
    1933          126 :       if (old_state == m_start)
    1934           21 :         sm_ctxt.set_next_state (fd_sval, m_new_unknown_socket);
    1935              :     }
    1936              : 
    1937              :   /* Passing NULL as the address will lead to failure.  */
    1938            9 :   if (successful)
    1939          112 :     if (address_sval->all_zeroes_p ())
    1940              :       return false;
    1941              : 
    1942              :   return true;
    1943              : }
    1944              : 
    1945              : /* Update the model and fd state for an outcome of a call to "bind",
    1946              :    where SUCCESSFUL indicate which of the two outcomes.
    1947              :    Return true if the outcome is feasible, or false to reject it.  */
    1948              : 
    1949              : bool
    1950          222 : fd_state_machine::on_bind (const call_details &cd,
    1951              :                            bool successful,
    1952              :                            sm_context &sm_ctxt,
    1953              :                            const extrinsic_state &) const
    1954              : {
    1955          222 :   const svalue *fd_sval = cd.get_arg_svalue (0);
    1956          222 :   region_model *model = cd.get_model ();
    1957          222 :   state_t old_state = sm_ctxt.get_state (fd_sval);
    1958              : 
    1959          222 :   if (!check_for_new_socket_fd (cd, successful, sm_ctxt,
    1960              :                                 fd_sval, old_state,
    1961              :                                 EXPECTED_PHASE_CAN_BIND))
    1962              :     return false;
    1963              : 
    1964          198 :   if (successful)
    1965              :     {
    1966           87 :       state_t next_state = nullptr;
    1967           87 :       if (old_state == m_new_stream_socket)
    1968           13 :         next_state = m_bound_stream_socket;
    1969           74 :       else if (old_state == m_new_datagram_socket)
    1970           14 :         next_state = m_bound_datagram_socket;
    1971           60 :       else if (old_state == m_new_unknown_socket)
    1972           38 :         next_state = m_bound_unknown_socket;
    1973           22 :       else if (old_state == m_start
    1974            8 :                || old_state == m_constant_fd)
    1975           20 :         next_state = m_bound_unknown_socket;
    1976            2 :       else if (old_state == m_stop)
    1977              :         next_state = m_stop;
    1978              :       else
    1979            0 :         gcc_unreachable ();
    1980           87 :       sm_ctxt.set_next_state (fd_sval, next_state);
    1981           87 :       model->update_for_zero_return (cd, true);
    1982              :     }
    1983              :   else
    1984              :     {
    1985              :       /* Return -1; set errno.  */
    1986          111 :       model->update_for_int_cst_return (cd, -1, true);
    1987          111 :       model->set_errno (cd);
    1988              :     }
    1989              : 
    1990              :   return true;
    1991              : }
    1992              : 
    1993              : /* Update the model and fd state for an outcome of a call to "listen",
    1994              :    where SUCCESSFUL indicate which of the two outcomes.
    1995              :    Return true if the outcome is feasible, or false to reject it.  */
    1996              : 
    1997              : bool
    1998          108 : fd_state_machine::on_listen (const call_details &cd,
    1999              :                              bool successful,
    2000              :                              sm_context &sm_ctxt,
    2001              :                              const extrinsic_state &) const
    2002              : {
    2003          108 :   const svalue *fd_sval = cd.get_arg_svalue (0);
    2004          108 :   region_model *model = cd.get_model ();
    2005          108 :   state_t old_state = sm_ctxt.get_state (fd_sval);
    2006              : 
    2007              :   /* We expect a stream socket that's had "bind" called on it.  */
    2008          108 :   if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, old_state))
    2009              :     return false;
    2010          107 :   if (!(old_state == m_start
    2011           93 :         || old_state == m_constant_fd
    2012           89 :         || old_state == m_stop
    2013           89 :         || old_state == m_invalid
    2014           88 :         || old_state == m_bound_stream_socket
    2015           76 :         || old_state == m_bound_unknown_socket
    2016              :         /* Assume it's OK to call "listen" more than once.  */
    2017           48 :         || old_state == m_listening_stream_socket))
    2018              :     {
    2019              :       /* Complain about fncall on wrong type or in wrong phase.  */
    2020           46 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval);
    2021           46 :       if (is_stream_socket_fd_p (old_state))
    2022           38 :         sm_ctxt.warn
    2023           38 :           (fd_sval,
    2024           38 :            std::make_unique<fd_phase_mismatch> (*this, diag_arg,
    2025           38 :                                                 cd.get_fndecl_for_call (),
    2026              :                                                 old_state,
    2027           76 :                                                 EXPECTED_PHASE_CAN_LISTEN));
    2028              :       else
    2029            8 :         sm_ctxt.warn
    2030            8 :           (fd_sval,
    2031            8 :            std::make_unique<fd_type_mismatch> (*this, diag_arg,
    2032            8 :                                                cd.get_fndecl_for_call (),
    2033              :                                                old_state,
    2034           16 :                                                EXPECTED_TYPE_STREAM_SOCKET));
    2035           46 :       if (successful)
    2036           23 :         return false;
    2037              :     }
    2038              : 
    2039           84 :   if (successful)
    2040              :     {
    2041           30 :       model->update_for_zero_return (cd, true);
    2042           30 :       sm_ctxt.set_next_state (fd_sval, m_listening_stream_socket);
    2043              :     }
    2044              :   else
    2045              :     {
    2046              :       /* Return -1; set errno.  */
    2047           54 :       model->update_for_int_cst_return (cd, -1, true);
    2048           54 :       model->set_errno (cd);
    2049           54 :       if (old_state == m_start)
    2050            7 :         sm_ctxt.set_next_state (fd_sval, m_bound_stream_socket);
    2051              :     }
    2052              : 
    2053              :   return true;
    2054              : }
    2055              : 
    2056              : /* Update the model and fd state for an outcome of a call to "accept",
    2057              :    where SUCCESSFUL indicate which of the two outcomes.
    2058              :    Return true if the outcome is feasible, or false to reject it.  */
    2059              : 
    2060              : bool
    2061           56 : fd_state_machine::on_accept (const call_details &cd,
    2062              :                              bool successful,
    2063              :                              sm_context &sm_ctxt,
    2064              :                              const extrinsic_state &) const
    2065              : {
    2066           56 :   const gcall &call = cd.get_call_stmt ();
    2067           56 :   const svalue *fd_sval = cd.get_arg_svalue (0);
    2068           56 :   const svalue *address_sval = cd.get_arg_svalue (1);
    2069           56 :   const svalue *len_ptr_sval = cd.get_arg_svalue (2);
    2070           56 :   region_model *model = cd.get_model ();
    2071           56 :   state_t old_state = sm_ctxt.get_state (fd_sval);
    2072              : 
    2073           56 :   if (!address_sval->all_zeroes_p ())
    2074              :     {
    2075           20 :       region_model_manager *mgr = model->get_manager ();
    2076              : 
    2077              :       /* We might have a union of various pointer types, rather than a
    2078              :          pointer type; cast to (void *) before dereferencing.  */
    2079           20 :       address_sval = mgr->get_or_create_cast (ptr_type_node, address_sval);
    2080              : 
    2081           20 :       const region *address_reg
    2082           20 :         = model->deref_rvalue (address_sval, cd.get_arg_tree (1),
    2083              :                                cd.get_ctxt ());
    2084           20 :       const region *len_reg
    2085           20 :         = model->deref_rvalue (len_ptr_sval, cd.get_arg_tree (2),
    2086              :                                cd.get_ctxt ());
    2087           20 :       const svalue *old_len_sval
    2088           20 :         = model->get_store_value (len_reg, cd.get_ctxt ());
    2089           20 :       tree len_ptr = cd.get_arg_tree (2);
    2090           20 :       tree star_len_ptr = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (len_ptr)),
    2091              :                                   len_ptr,
    2092           20 :                                   build_int_cst (TREE_TYPE (len_ptr), 0));
    2093           20 :       old_len_sval = model->check_for_poison (old_len_sval,
    2094              :                                               star_len_ptr,
    2095              :                                               len_reg,
    2096              :                                               cd.get_ctxt ());
    2097           20 :       if (successful)
    2098              :         {
    2099           10 :           conjured_purge p (model, cd.get_ctxt ());
    2100           10 :           const region *old_sized_address_reg
    2101           10 :             = mgr->get_sized_region (address_reg,
    2102              :                                      NULL_TREE,
    2103              :                                      old_len_sval);
    2104           10 :           const svalue *new_addr_sval
    2105           10 :             = mgr->get_or_create_conjured_svalue (NULL_TREE,
    2106              :                                                   &call,
    2107              :                                                   old_sized_address_reg,
    2108              :                                                   p);
    2109           10 :           model->set_value (old_sized_address_reg, new_addr_sval,
    2110              :                             cd.get_ctxt ());
    2111           10 :           const svalue *new_addr_len
    2112           10 :             = mgr->get_or_create_conjured_svalue (NULL_TREE,
    2113              :                                                   &call,
    2114              :                                                   len_reg,
    2115              :                                                   p);
    2116           10 :           model->set_value (len_reg, new_addr_len, cd.get_ctxt ());
    2117              :         }
    2118              :     }
    2119              : 
    2120              :   /* We expect a stream socket in the "listening" state.  */
    2121           56 :   if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, old_state))
    2122              :     return false;
    2123              : 
    2124           56 :   if (old_state == m_start || old_state == m_constant_fd)
    2125              :     /* If we were in the start state (or a constant), assume we had the
    2126              :        expected state.  */
    2127           24 :     sm_ctxt.set_next_state (fd_sval, m_listening_stream_socket);
    2128           32 :   else if (old_state == m_stop)
    2129              :     {
    2130              :       /* No further complaints.  */
    2131              :     }
    2132           32 :   else if (old_state != m_listening_stream_socket)
    2133              :     {
    2134              :       /* Complain about fncall on wrong type or in wrong phase.  */
    2135            6 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval);
    2136            6 :       if (is_stream_socket_fd_p (old_state))
    2137            4 :         sm_ctxt.warn
    2138            4 :           (fd_sval,
    2139            4 :            std::make_unique<fd_phase_mismatch> (*this, diag_arg,
    2140            4 :                                                 cd.get_fndecl_for_call (),
    2141              :                                                 old_state,
    2142            8 :                                                 EXPECTED_PHASE_CAN_ACCEPT));
    2143              :       else
    2144            2 :         sm_ctxt.warn
    2145            2 :           (fd_sval,
    2146            2 :            std::make_unique<fd_type_mismatch> (*this, diag_arg,
    2147            2 :                                                cd.get_fndecl_for_call (),
    2148              :                                                old_state,
    2149            4 :                                                EXPECTED_TYPE_STREAM_SOCKET));
    2150            6 :       if (successful)
    2151            3 :         return false;
    2152              :     }
    2153              : 
    2154           53 :   if (successful)
    2155              :     {
    2156              :       /* Return new conjured FD in "connected" state.  */
    2157           25 :       if (gimple_call_lhs (&call))
    2158              :         {
    2159           24 :           conjured_purge p (model, cd.get_ctxt ());
    2160           24 :           region_model_manager *mgr = model->get_manager ();
    2161           24 :           const svalue *new_fd
    2162           24 :             = mgr->get_or_create_conjured_svalue (integer_type_node,
    2163              :                                                   &call,
    2164              :                                                   cd.get_lhs_region (),
    2165              :                                                   p);
    2166           24 :           if (!add_constraint_ge_zero (model, new_fd, cd.get_ctxt ()))
    2167            0 :             return false;
    2168           24 :           sm_ctxt.on_transition (new_fd, m_start, m_connected_stream_socket);
    2169           24 :           model->set_value (cd.get_lhs_region (), new_fd, cd.get_ctxt ());
    2170              :         }
    2171              :       else
    2172            1 :         sm_ctxt.warn (NULL_TREE,
    2173            1 :                       std::make_unique<fd_leak> (*this, NULL_TREE, nullptr));
    2174              :     }
    2175              :   else
    2176              :     {
    2177              :       /* Return -1; set errno.  */
    2178           28 :       model->update_for_int_cst_return (cd, -1, true);
    2179           28 :       model->set_errno (cd);
    2180              :     }
    2181              : 
    2182              :   return true;
    2183              : }
    2184              : 
    2185              : /* Update the model and fd state for an outcome of a call to "connect",
    2186              :    where SUCCESSFUL indicate which of the two outcomes.
    2187              :    Return true if the outcome is feasible, or false to reject it.  */
    2188              : 
    2189              : bool
    2190           48 : fd_state_machine::on_connect (const call_details &cd,
    2191              :                               bool successful,
    2192              :                               sm_context &sm_ctxt,
    2193              :                               const extrinsic_state &) const
    2194              : {
    2195           48 :   const svalue *fd_sval = cd.get_arg_svalue (0);
    2196           48 :   region_model *model = cd.get_model ();
    2197           48 :   state_t old_state = sm_ctxt.get_state (fd_sval);
    2198              : 
    2199           48 :   if (!check_for_new_socket_fd (cd, successful, sm_ctxt,
    2200              :                                 fd_sval, old_state,
    2201              :                                 EXPECTED_PHASE_CAN_CONNECT))
    2202              :     return false;
    2203              : 
    2204           45 :   if (successful)
    2205              :     {
    2206           21 :       model->update_for_zero_return (cd, true);
    2207           21 :       state_t next_state = nullptr;
    2208           21 :       if (old_state == m_new_stream_socket)
    2209            3 :         next_state = m_connected_stream_socket;
    2210           18 :       else if (old_state == m_new_datagram_socket)
    2211              :         /* It's legal to call connect on a datagram socket, potentially
    2212              :            more than once.  We don't transition states for this.  */
    2213              :         next_state = m_new_datagram_socket;
    2214           18 :       else if (old_state == m_new_unknown_socket)
    2215            7 :         next_state = m_stop;
    2216           11 :       else if (old_state == m_start
    2217            6 :                || old_state == m_constant_fd)
    2218           11 :         next_state = m_stop;
    2219            0 :       else if (old_state == m_stop)
    2220              :         next_state = m_stop;
    2221              :       else
    2222            0 :         gcc_unreachable ();
    2223           21 :       sm_ctxt.set_next_state (fd_sval, next_state);
    2224              :     }
    2225              :   else
    2226              :     {
    2227              :       /* Return -1; set errno.  */
    2228           24 :       model->update_for_int_cst_return (cd, -1, true);
    2229           24 :       model->set_errno (cd);
    2230              :       /* TODO: perhaps transition to a failed state, since the
    2231              :          portable way to handle a failed "connect" is to close
    2232              :          the socket and try again with a new socket.  */
    2233              :     }
    2234              : 
    2235              :   return true;
    2236              : }
    2237              : 
    2238              : void
    2239        34595 : fd_state_machine::on_condition (sm_context &sm_ctxt,
    2240              :                                 const svalue *lhs,
    2241              :                                 enum tree_code op,
    2242              :                                 const svalue *rhs) const
    2243              : {
    2244        34595 :   if (tree cst = rhs->maybe_get_constant ())
    2245              :     {
    2246        27366 :       if (TREE_CODE (cst) == INTEGER_CST)
    2247              :         {
    2248        27358 :           int val = TREE_INT_CST_LOW (cst);
    2249        27358 :           if (val == -1)
    2250              :             {
    2251          560 :               if (op == NE_EXPR)
    2252          256 :                 make_valid_transitions_on_condition (sm_ctxt, lhs);
    2253              : 
    2254          304 :               else if (op == EQ_EXPR)
    2255          256 :                 make_invalid_transitions_on_condition (sm_ctxt, lhs);
    2256              :             }
    2257              :         }
    2258              :     }
    2259              : 
    2260        34595 :   if (rhs->all_zeroes_p ())
    2261              :     {
    2262        21180 :       if (op == GE_EXPR)
    2263          681 :         make_valid_transitions_on_condition (sm_ctxt, lhs);
    2264        20499 :       else if (op == LT_EXPR)
    2265          480 :         make_invalid_transitions_on_condition (sm_ctxt, lhs);
    2266              :     }
    2267        34595 : }
    2268              : 
    2269              : void
    2270          937 : fd_state_machine::make_valid_transitions_on_condition (sm_context &sm_ctxt,
    2271              :                                                        const svalue *lhs) const
    2272              : {
    2273          937 :   sm_ctxt.on_transition (lhs, m_unchecked_read_write,
    2274          937 :                          m_valid_read_write);
    2275          937 :   sm_ctxt.on_transition (lhs, m_unchecked_read_only,
    2276          937 :                          m_valid_read_only);
    2277          937 :   sm_ctxt.on_transition (lhs, m_unchecked_write_only,
    2278          937 :                          m_valid_write_only);
    2279          937 : }
    2280              : 
    2281              : void
    2282          736 : fd_state_machine::
    2283              : make_invalid_transitions_on_condition (sm_context &sm_ctxt,
    2284              :                                        const svalue *lhs) const
    2285              : {
    2286          736 :   sm_ctxt.on_transition (lhs, m_unchecked_read_write, m_invalid);
    2287          736 :   sm_ctxt.on_transition (lhs, m_unchecked_read_only, m_invalid);
    2288          736 :   sm_ctxt.on_transition (lhs, m_unchecked_write_only, m_invalid);
    2289          736 : }
    2290              : 
    2291              : bool
    2292      1422022 : fd_state_machine::can_purge_p (state_t s) const
    2293              : {
    2294      2843944 :   if (is_unchecked_fd_p (s)
    2295      1422814 :       || is_valid_fd_p (s)
    2296      1421876 :       || is_socket_fd_p (s))
    2297          938 :     return false;
    2298              :   else
    2299              :     return true;
    2300              : }
    2301              : 
    2302              : std::unique_ptr<pending_diagnostic>
    2303          117 : fd_state_machine::on_leak (tree var,
    2304              :                            const program_state *,
    2305              :                            const program_state *new_state) const
    2306              : {
    2307          117 :   return std::make_unique<fd_leak> (*this, var, new_state);
    2308              : }
    2309              : } // namespace
    2310              : 
    2311              : std::unique_ptr<state_machine>
    2312         3377 : make_fd_state_machine (logger *logger)
    2313              : {
    2314         3377 :   return std::make_unique<fd_state_machine> (logger);
    2315              : }
    2316              : 
    2317              : static bool
    2318         1518 : get_fd_state (region_model_context *ctxt,
    2319              :               sm_state_map **out_smap,
    2320              :               const fd_state_machine **out_sm,
    2321              :               unsigned *out_sm_idx,
    2322              :               std::unique_ptr<sm_context> *out_sm_context)
    2323              : {
    2324         1518 :   if (!ctxt)
    2325              :     return false;
    2326              : 
    2327          766 :   const state_machine *sm;
    2328          766 :   if (!ctxt->get_fd_map (out_smap, &sm, out_sm_idx, out_sm_context))
    2329              :     return false;
    2330              : 
    2331          766 :   gcc_assert (sm);
    2332              : 
    2333          766 :   *out_sm = (const fd_state_machine *)sm;
    2334          766 :   return true;
    2335              : }
    2336              : 
    2337              : /* Specialcase hook for handling pipe, for use by
    2338              :    kf_pipe::success::update_model.  */
    2339              : 
    2340              : void
    2341           34 : region_model::mark_as_valid_fd (const svalue *sval, region_model_context *ctxt)
    2342              : {
    2343           34 :   sm_state_map *smap;
    2344           34 :   const fd_state_machine *fd_sm;
    2345           34 :   if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, nullptr))
    2346           16 :     return;
    2347           18 :   const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2348           18 :   if (!ext_state)
    2349              :     return;
    2350           18 :   fd_sm->mark_as_valid_fd (this, smap, sval, *ext_state);
    2351              : }
    2352              : 
    2353              : /* Handle calls to "socket".
    2354              :    See e.g. https://man7.org/linux/man-pages/man3/socket.3p.html  */
    2355              : 
    2356         3377 : class kf_socket : public known_function
    2357              : {
    2358              : public:
    2359              :   class outcome_of_socket : public succeed_or_fail_call_info
    2360              :   {
    2361              :   public:
    2362          214 :     outcome_of_socket (const call_details &cd, bool success)
    2363          214 :     : succeed_or_fail_call_info (cd, success)
    2364              :     {}
    2365              : 
    2366          712 :     bool update_model (region_model *model,
    2367              :                        const exploded_edge *,
    2368              :                        region_model_context *ctxt) const final override
    2369              :     {
    2370          712 :       const call_details cd (get_call_details (model, ctxt));
    2371          712 :       sm_state_map *smap;
    2372          712 :       const fd_state_machine *fd_sm;
    2373          712 :       std::unique_ptr<sm_context> sm_ctxt;
    2374          712 :       if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt))
    2375              :         {
    2376          498 :           cd.set_any_lhs_with_defaults ();
    2377          498 :           return true;
    2378              :         }
    2379          214 :       const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2380          214 :       if (!ext_state)
    2381              :         {
    2382            0 :           cd.set_any_lhs_with_defaults ();
    2383            0 :           return true;
    2384              :         }
    2385              : 
    2386          214 :       return fd_sm->on_socket (cd, m_success, *(sm_ctxt.get ()), *ext_state);
    2387          712 :     }
    2388              :   };
    2389              : 
    2390          749 :   bool matches_call_types_p (const call_details &cd) const final override
    2391              :   {
    2392          749 :     return cd.num_args () == 3;
    2393              :   }
    2394              : 
    2395          107 :   void impl_call_post (const call_details &cd) const final override
    2396              :   {
    2397          107 :     if (cd.get_ctxt ())
    2398              :       {
    2399          107 :         cd.get_ctxt ()->bifurcate
    2400          107 :           (std::make_unique<outcome_of_socket> (cd, false));
    2401          107 :         cd.get_ctxt ()->bifurcate
    2402          107 :           (std::make_unique<outcome_of_socket> (cd, true));
    2403          107 :         cd.get_ctxt ()->terminate_path ();
    2404              :       }
    2405          107 :   }
    2406              : };
    2407              : 
    2408              : /* Handle calls to "bind".
    2409              :    See e.g. https://man7.org/linux/man-pages/man3/bind.3p.html  */
    2410              : 
    2411         3377 : class kf_bind : public known_function
    2412              : {
    2413              : public:
    2414              :   class outcome_of_bind : public succeed_or_fail_call_info
    2415              :   {
    2416              :   public:
    2417          222 :     outcome_of_bind (const call_details &cd, bool success)
    2418          222 :     : succeed_or_fail_call_info (cd, success)
    2419              :     {}
    2420              : 
    2421          309 :     bool update_model (region_model *model,
    2422              :                        const exploded_edge *,
    2423              :                        region_model_context *ctxt) const final override
    2424              :     {
    2425          309 :       const call_details cd (get_call_details (model, ctxt));
    2426          309 :       sm_state_map *smap;
    2427          309 :       const fd_state_machine *fd_sm;
    2428          309 :       std::unique_ptr<sm_context> sm_ctxt;
    2429          309 :       if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt))
    2430              :         {
    2431           87 :           cd.set_any_lhs_with_defaults ();
    2432           87 :           return true;
    2433              :         }
    2434          222 :       const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2435          222 :       if (!ext_state)
    2436              :         {
    2437            0 :           cd.set_any_lhs_with_defaults ();
    2438            0 :           return true;
    2439              :         }
    2440          222 :       return fd_sm->on_bind (cd, m_success, *sm_ctxt.get (), *ext_state);
    2441          309 :     }
    2442              :   };
    2443              : 
    2444          820 :   bool matches_call_types_p (const call_details &cd) const final override
    2445              :   {
    2446          820 :     return (cd.num_args () == 3 && cd.arg_is_pointer_p (1));
    2447              :   }
    2448              : 
    2449          111 :   void impl_call_post (const call_details &cd) const final override
    2450              :   {
    2451          111 :     if (cd.get_ctxt ())
    2452              :       {
    2453          111 :         cd.get_ctxt ()->bifurcate
    2454          111 :           (std::make_unique<outcome_of_bind> (cd, false));
    2455          111 :         cd.get_ctxt ()->bifurcate
    2456          111 :           (std::make_unique<outcome_of_bind> (cd, true));
    2457          111 :         cd.get_ctxt ()->terminate_path ();
    2458              :       }
    2459          111 :   }
    2460              : };
    2461              : 
    2462              : /* Handle calls to "listen".
    2463              :    See e.g. https://man7.org/linux/man-pages/man3/listen.3p.html  */
    2464              : 
    2465         3377 : class kf_listen : public known_function
    2466              : {
    2467              :   class outcome_of_listen : public succeed_or_fail_call_info
    2468              :   {
    2469              :   public:
    2470          108 :     outcome_of_listen (const call_details &cd, bool success)
    2471          108 :     : succeed_or_fail_call_info (cd, success)
    2472              :     {}
    2473              : 
    2474          118 :     bool update_model (region_model *model,
    2475              :                        const exploded_edge *,
    2476              :                        region_model_context *ctxt) const final override
    2477              :     {
    2478          118 :       const call_details cd (get_call_details (model, ctxt));
    2479          118 :       sm_state_map *smap;
    2480          118 :       const fd_state_machine *fd_sm;
    2481          118 :       std::unique_ptr<sm_context> sm_ctxt;
    2482          118 :       if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt))
    2483              :         {
    2484           10 :           cd.set_any_lhs_with_defaults ();
    2485           10 :           return true;
    2486              :         }
    2487          108 :       const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2488          108 :       if (!ext_state)
    2489              :         {
    2490            0 :           cd.set_any_lhs_with_defaults ();
    2491            0 :           return true;
    2492              :         }
    2493              : 
    2494          108 :       return fd_sm->on_listen (cd, m_success, *sm_ctxt.get (), *ext_state);
    2495          118 :     }
    2496              :   };
    2497              : 
    2498          378 :   bool matches_call_types_p (const call_details &cd) const final override
    2499              :   {
    2500          378 :     return cd.num_args () == 2;
    2501              :   }
    2502              : 
    2503           54 :   void impl_call_post (const call_details &cd) const final override
    2504              :   {
    2505           54 :     if (cd.get_ctxt ())
    2506              :       {
    2507           54 :         cd.get_ctxt ()->bifurcate
    2508           54 :           (std::make_unique<outcome_of_listen> (cd, false));
    2509           54 :         cd.get_ctxt ()->bifurcate
    2510           54 :           (std::make_unique<outcome_of_listen> (cd, true));
    2511           54 :         cd.get_ctxt ()->terminate_path ();
    2512              :       }
    2513           54 :   }
    2514              : };
    2515              : 
    2516              : /* Handle calls to "accept".
    2517              :    See e.g. https://man7.org/linux/man-pages/man3/accept.3p.html  */
    2518              : 
    2519         3377 : class kf_accept : public known_function
    2520              : {
    2521              :   class outcome_of_accept : public succeed_or_fail_call_info
    2522              :   {
    2523              :   public:
    2524           56 :     outcome_of_accept (const call_details &cd, bool success)
    2525           56 :     : succeed_or_fail_call_info (cd, success)
    2526              :     {}
    2527              : 
    2528           70 :     bool update_model (region_model *model,
    2529              :                        const exploded_edge *,
    2530              :                        region_model_context *ctxt) const final override
    2531              :     {
    2532           70 :       const call_details cd (get_call_details (model, ctxt));
    2533           70 :       sm_state_map *smap;
    2534           70 :       const fd_state_machine *fd_sm;
    2535           70 :       std::unique_ptr<sm_context> sm_ctxt;
    2536           70 :       if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt))
    2537              :         {
    2538           14 :           cd.set_any_lhs_with_defaults ();
    2539           14 :           return true;
    2540              :         }
    2541           56 :       const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2542           56 :       if (!ext_state)
    2543              :         {
    2544            0 :           cd.set_any_lhs_with_defaults ();
    2545            0 :           return true;
    2546              :         }
    2547              : 
    2548           56 :       return fd_sm->on_accept (cd, m_success, *sm_ctxt.get (), *ext_state);
    2549           70 :     }
    2550              :   };
    2551              : 
    2552          245 :   bool matches_call_types_p (const call_details &cd) const final override
    2553              :   {
    2554          245 :     return (cd.num_args () == 3
    2555          245 :             && cd.arg_is_pointer_p (1)
    2556          441 :             && cd.arg_is_pointer_p (2));
    2557              :   }
    2558              : 
    2559           28 :   void impl_call_post (const call_details &cd) const final override
    2560              :   {
    2561           28 :     if (cd.get_ctxt ())
    2562              :       {
    2563           28 :         cd.get_ctxt ()->bifurcate
    2564           28 :           (std::make_unique<outcome_of_accept> (cd, false));
    2565           28 :         cd.get_ctxt ()->bifurcate
    2566           28 :           (std::make_unique<outcome_of_accept> (cd, true));
    2567           28 :         cd.get_ctxt ()->terminate_path ();
    2568              :       }
    2569           28 :   }
    2570              : };
    2571              : 
    2572              : /* Handle calls to "connect".
    2573              :    See e.g. https://man7.org/linux/man-pages/man3/connect.3p.html  */
    2574              : 
    2575         3377 : class kf_connect : public known_function
    2576              : {
    2577              : public:
    2578              :   class outcome_of_connect : public succeed_or_fail_call_info
    2579              :   {
    2580              :   public:
    2581           48 :     outcome_of_connect (const call_details &cd, bool success)
    2582           48 :     : succeed_or_fail_call_info (cd, success)
    2583              :     {}
    2584              : 
    2585          168 :     bool update_model (region_model *model,
    2586              :                        const exploded_edge *,
    2587              :                        region_model_context *ctxt) const final override
    2588              :     {
    2589          168 :       const call_details cd (get_call_details (model, ctxt));
    2590          168 :       sm_state_map *smap;
    2591          168 :       const fd_state_machine *fd_sm;
    2592          168 :       std::unique_ptr<sm_context> sm_ctxt;
    2593          168 :       if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt))
    2594              :         {
    2595          120 :           cd.set_any_lhs_with_defaults ();
    2596          120 :           return true;
    2597              :         }
    2598           48 :       const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2599           48 :       if (!ext_state)
    2600              :         {
    2601            0 :           cd.set_any_lhs_with_defaults ();
    2602            0 :           return true;
    2603              :         }
    2604              : 
    2605           48 :       return fd_sm->on_connect (cd, m_success, *sm_ctxt.get (), *ext_state);
    2606          168 :     }
    2607              :   };
    2608              : 
    2609          168 :   bool matches_call_types_p (const call_details &cd) const final override
    2610              :   {
    2611          168 :     return (cd.num_args () == 3
    2612          168 :             && cd.arg_is_pointer_p (1));
    2613              :   }
    2614              : 
    2615           24 :   void impl_call_post (const call_details &cd) const final override
    2616              :   {
    2617           24 :     if (cd.get_ctxt ())
    2618              :       {
    2619           24 :         cd.get_ctxt ()->bifurcate
    2620           24 :           (std::make_unique<outcome_of_connect> (cd, false));
    2621           24 :         cd.get_ctxt ()->bifurcate
    2622           24 :           (std::make_unique<outcome_of_connect> (cd, true));
    2623           24 :         cd.get_ctxt ()->terminate_path ();
    2624              :       }
    2625           24 :   }
    2626              : };
    2627              : 
    2628              : /* Handler for "isatty"".
    2629              :    See e.g. https://man7.org/linux/man-pages/man3/isatty.3.html  */
    2630              : 
    2631         3377 : class kf_isatty : public known_function
    2632              : {
    2633              :   class outcome_of_isatty : public succeed_or_fail_call_info
    2634              :   {
    2635              :   public:
    2636          200 :     outcome_of_isatty (const call_details &cd, bool success)
    2637          200 :     : succeed_or_fail_call_info (cd, success)
    2638              :     {}
    2639              : 
    2640          214 :     bool update_model (region_model *model,
    2641              :                        const exploded_edge *,
    2642              :                        region_model_context *ctxt) const final override
    2643              :     {
    2644          214 :       const call_details cd (get_call_details (model, ctxt));
    2645              : 
    2646          214 :       if (m_success)
    2647              :         {
    2648              :           /* Return 1.  */
    2649          107 :           model->update_for_int_cst_return (cd, 1, true);
    2650              :         }
    2651              :       else
    2652              :         {
    2653              :           /* Return 0; set errno.  */
    2654          107 :           model->update_for_int_cst_return (cd, 0, true);
    2655          107 :           model->set_errno (cd);
    2656              :         }
    2657              : 
    2658          214 :       return feasible_p (cd, ctxt);
    2659              :     }
    2660              : 
    2661              :   private:
    2662          214 :     bool feasible_p (const call_details &cd,
    2663              :                      region_model_context *ctxt) const
    2664              :     {
    2665          214 :       if (m_success)
    2666              :         {
    2667              :           /* Can't be "success" on a closed/invalid fd.  */
    2668          107 :           sm_state_map *smap;
    2669          107 :           const fd_state_machine *fd_sm;
    2670          107 :           std::unique_ptr<sm_context> sm_ctxt;
    2671          107 :           if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt))
    2672              :             return true;
    2673          100 :           const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2674          100 :           if (!ext_state)
    2675              :             return true;
    2676              : 
    2677          100 :           const svalue *fd_sval = cd.get_arg_svalue (0);
    2678          100 :           state_machine::state_t old_state = sm_ctxt->get_state (fd_sval);
    2679              : 
    2680          100 :           if (fd_sm->is_closed_fd_p (old_state)
    2681          100 :               || old_state == fd_sm->m_invalid)
    2682              :             return false;
    2683          107 :         }
    2684              :       return true;
    2685              :     }
    2686              :   }; // class outcome_of_isatty
    2687              : 
    2688              : public:
    2689          700 :   bool matches_call_types_p (const call_details &cd) const final override
    2690              :   {
    2691          700 :     return cd.num_args () == 1;
    2692              :   }
    2693              : 
    2694          100 :   void impl_call_post (const call_details &cd) const final override
    2695              :   {
    2696          100 :     if (cd.get_ctxt ())
    2697              :       {
    2698          100 :         cd.get_ctxt ()->bifurcate
    2699          100 :           (std::make_unique<outcome_of_isatty> (cd, false));
    2700          100 :         cd.get_ctxt ()->bifurcate
    2701          100 :           (std::make_unique<outcome_of_isatty> (cd, true));
    2702          100 :         cd.get_ctxt ()->terminate_path ();
    2703              :       }
    2704          100 :   }
    2705              : };
    2706              : 
    2707              : /* Handler for calls to "pipe" and "pipe2".
    2708              :    See e.g. https://www.man7.org/linux/man-pages/man2/pipe.2.html  */
    2709              : 
    2710              : class kf_pipe : public known_function
    2711              : {
    2712              :   class failure : public failed_call_info
    2713              :   {
    2714              :   public:
    2715            9 :     failure (const call_details &cd) : failed_call_info (cd) {}
    2716              : 
    2717           21 :     bool update_model (region_model *model,
    2718              :                        const exploded_edge *,
    2719              :                        region_model_context *ctxt) const final override
    2720              :     {
    2721              :       /* Return -1; everything else is unchanged.  */
    2722           21 :       const call_details cd (get_call_details (model, ctxt));
    2723           21 :       model->update_for_int_cst_return (cd, -1, true);
    2724           21 :       return true;
    2725              :     }
    2726              :   };
    2727              : 
    2728              :   class success : public success_call_info
    2729              :   {
    2730              :   public:
    2731            9 :     success (const call_details &cd) : success_call_info (cd) {}
    2732              : 
    2733           17 :     bool update_model (region_model *model,
    2734              :                        const exploded_edge *,
    2735              :                        region_model_context *ctxt) const final override
    2736              :     {
    2737           17 :       const call_details cd (get_call_details (model, ctxt));
    2738              : 
    2739              :       /* Return 0.  */
    2740           17 :       model->update_for_zero_return (cd, true);
    2741              : 
    2742              :       /* Update fd array.  */
    2743           17 :       region_model_manager *mgr = cd.get_manager ();
    2744           17 :       tree arr_tree = cd.get_arg_tree (0);
    2745           17 :       const svalue *arr_sval = cd.get_arg_svalue (0);
    2746           51 :       for (int idx = 0; idx < 2; idx++)
    2747              :         {
    2748           34 :           const region *arr_reg
    2749           34 :             = model->deref_rvalue (arr_sval, arr_tree, cd.get_ctxt ());
    2750           34 :           const svalue *idx_sval
    2751           34 :             = mgr->get_or_create_int_cst (integer_type_node, idx);
    2752           34 :           const region *element_reg
    2753           34 :             = mgr->get_element_region (arr_reg, integer_type_node, idx_sval);
    2754           34 :           conjured_purge p (model, cd.get_ctxt ());
    2755           34 :           const svalue *fd_sval
    2756           34 :             = mgr->get_or_create_conjured_svalue (integer_type_node,
    2757           34 :                                                   &cd.get_call_stmt (),
    2758              :                                                   element_reg,
    2759              :                                                   p);
    2760           34 :           model->set_value (element_reg, fd_sval, cd.get_ctxt ());
    2761           34 :           model->mark_as_valid_fd (fd_sval, cd.get_ctxt ());
    2762              :         }
    2763           17 :       return true;
    2764              :     }
    2765              :   };
    2766              : 
    2767              : public:
    2768         6754 :   kf_pipe (unsigned num_args)
    2769         6754 :   : m_num_args (num_args)
    2770              :   {
    2771         6754 :     gcc_assert (num_args > 0);
    2772         6754 :   }
    2773              : 
    2774           91 :   bool matches_call_types_p (const call_details &cd) const final override
    2775              :   {
    2776           91 :     return (cd.num_args () == m_num_args && cd.arg_is_pointer_p (0));
    2777              :   }
    2778              : 
    2779            9 :   void impl_call_post (const call_details &cd) const final override
    2780              :   {
    2781            9 :     if (cd.get_ctxt ())
    2782              :       {
    2783            9 :         cd.get_ctxt ()->bifurcate
    2784            9 :           (std::make_unique<failure> (cd));
    2785            9 :         cd.get_ctxt ()->bifurcate
    2786            9 :           (std::make_unique<success> (cd));
    2787            9 :         cd.get_ctxt ()->terminate_path ();
    2788              :       }
    2789            9 :   }
    2790              : 
    2791              : private:
    2792              :   unsigned m_num_args;
    2793              : };
    2794              : 
    2795              : /* Handler for "read".
    2796              :      ssize_t read(int fildes, void *buf, size_t nbyte);
    2797              :    See e.g. https://man7.org/linux/man-pages/man2/read.2.html   */
    2798              : 
    2799         3377 : class kf_read : public known_function
    2800              : {
    2801              : public:
    2802          360 :   bool matches_call_types_p (const call_details &cd) const final override
    2803              :   {
    2804          360 :     return (cd.num_args () == 3
    2805          360 :             && cd.arg_is_pointer_p (1)
    2806          720 :             && cd.arg_is_size_p (2));
    2807              :   }
    2808              : 
    2809              :   /* For now, assume that any call to "read" fully clobbers the buffer
    2810              :      passed in.  This isn't quite correct (e.g. errors, partial reads;
    2811              :      see PR analyzer/108689), but at least stops us falsely complaining
    2812              :      about the buffer being uninitialized.  */
    2813           59 :   void impl_call_pre (const call_details &cd) const final override
    2814              :   {
    2815           59 :     region_model *model = cd.get_model ();
    2816           59 :     const svalue *ptr_sval = cd.get_arg_svalue (1);
    2817           59 :     if (const region *reg = ptr_sval->maybe_get_region ())
    2818              :       {
    2819           40 :         const region *base_reg = reg->get_base_region ();
    2820           40 :         const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
    2821           40 :         model->set_value (base_reg, new_sval, cd.get_ctxt ());
    2822              :       }
    2823           59 :     cd.set_any_lhs_with_defaults ();
    2824           59 :   }
    2825              : };
    2826              : 
    2827              : 
    2828              : /* Populate KFM with instances of known functions relating to
    2829              :    file descriptors.  */
    2830              : 
    2831              : void
    2832         3377 : register_known_fd_functions (known_function_manager &kfm)
    2833              : {
    2834         3377 :   kfm.add ("accept", std::make_unique<kf_accept> ());
    2835         3377 :   kfm.add ("bind", std::make_unique<kf_bind> ());
    2836         3377 :   kfm.add ("connect", std::make_unique<kf_connect> ());
    2837         3377 :   kfm.add ("isatty", std::make_unique<kf_isatty> ());
    2838         3377 :   kfm.add ("listen", std::make_unique<kf_listen> ());
    2839         3377 :   kfm.add ("pipe", std::make_unique<kf_pipe> (1));
    2840         3377 :   kfm.add ("pipe2", std::make_unique<kf_pipe> (2));
    2841         3377 :   kfm.add ("read", std::make_unique<kf_read> ());
    2842         3377 :   kfm.add ("socket", std::make_unique<kf_socket> ());
    2843         3377 : }
    2844              : 
    2845              : } // namespace ana
    2846              : 
    2847              : #endif // 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.