[v2] analyzer: add warnings relating to sockets [PR106140]

Message ID 20221112032740.2724091-1-dmalcolm@redhat.com
State Committed
Headers
Series [v2] analyzer: add warnings relating to sockets [PR106140] |

Commit Message

David Malcolm Nov. 12, 2022, 3:27 a.m. UTC
  Changed in v2: ported doc changes from texinfo to sphinx

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

I can self-approve this patch, but it depends on the named constants
patch here:
  * [PATCH v2] c, analyzer: support named constants in analyzer [PR106302]
    * https://gcc.gnu.org/pipermail/gcc-patches/2022-November/605835.html
which requires review of the C frontend changes.


This patch generalizes the analyzer's file descriptor state machine
so that it tracks the states of sockets.

It adds two new warnings relating to misuses of socket APIs:
* -Wanalyzer-fd-phase-mismatch (e.g. calling 'accept' on a socket
before calling 'listen' on it)
* -Wanalyzer-fd-type-mismatch (e.g. using a stream socket operation
on a datagram socket)

gcc/analyzer/ChangeLog:
	PR analyzer/106140
	* analyzer-language.cc (on_finish_translation_unit): Stash named
	constants "SOCK_STREAM" and "SOCK_DGRAM".
	* analyzer.opt (Wanalyzer-fd-phase-mismatch): New.
	(Wanalyzer-fd-type-mismatch): New.
	* engine.cc (impl_region_model_context::get_state_map_by_name):
	Add "out_sm_context" param.  Allow out_sm_idx to be NULL.
	* exploded-graph.h
	(impl_region_model_context::get_state_map_by_name):
	Add "out_sm_context" param.
	* region-model-impl-calls.cc (region_model::impl_call_accept): New.
	(region_model::impl_call_bind): New.
	(region_model::impl_call_connect): New.
	(region_model::impl_call_listen): New.
	(region_model::impl_call_socket): New.
	* region-model.cc (region_model::on_call_pre): Special-case
	"bind".
	(region_model::on_call_post): Special-case "accept", "bind",
	"connect", "listen", and "socket".
	* region-model.h (region_model::impl_call_accept): New decl.
	(region_model::impl_call_bind): New decl.
	(region_model::impl_call_connect): New decl.
	(region_model::impl_call_listen): New decl.
	(region_model::impl_call_socket): New decl.
	(region_model::on_socket): New decl.
	(region_model::on_bind): New decl.
	(region_model::on_listen): New decl.
	(region_model::on_accept): New decl.
	(region_model::on_connect): New decl.
	(region_model::add_constraint): Make public.
	(region_model::check_for_poison): Make public.
	(region_model_context::get_state_map_by_name): Add out_sm_context param.
	(region_model_context::get_fd_map): Likewise.
	(region_model_context::get_malloc_map): Likewise.
	(region_model_context::get_taint_map): Likewise.
	(noop_region_model_context::get_state_map_by_name): Likewise.
	(region_model_context_decorator::get_state_map_by_name): Likewise.
	* sm-fd.cc: Include "analyzer/supergraph.h" and
	"analyzer/analyzer-language.h".
	(enum expected_phase): New enum.
	(fd_state_machine::m_new_datagram_socket): New.
	(fd_state_machine::m_new_stream_socket): New.
	(fd_state_machine::m_new_unknown_socket): New.
	(fd_state_machine::m_bound_datagram_socket): New.
	(fd_state_machine::m_bound_stream_socket): New.
	(fd_state_machine::m_bound_unknown_socket): New.
	(fd_state_machine::m_listening_stream_socket): New.
	(fd_state_machine::m_m_connected_stream_socket): New.
	(fd_state_machine::m_SOCK_STREAM): New.
	(fd_state_machine::m_SOCK_DGRAM): New.
	(fd_diagnostic::describe_state_change): Handle socket states.
	(fd_diagnostic::get_meaning_for_state_change): Likewise.
	(class fd_phase_mismatch): New.
	(enum expected_type): New enum.
	(class fd_type_mismatch): New.
	(fd_state_machine::fd_state_machine): Initialize new states and
	stashed named constants.
	(fd_state_machine::is_socket_fd_p): New.
	(fd_state_machine::is_datagram_socket_fd_p): New.
	(fd_state_machine::is_stream_socket_fd_p): New.
	(fd_state_machine::on_close): Handle the socket states.
	(fd_state_machine::check_for_open_fd): Complain about fncalls on
	sockets in the wrong phase.  Support socket FDs.
	(add_constraint_ge_zero): New.
	(fd_state_machine::get_state_for_socket_type): New.
	(fd_state_machine::on_socket): New.
	(fd_state_machine::check_for_socket_fd): New.
	(fd_state_machine::check_for_new_socket_fd): New.
	(fd_state_machine::on_bind): New.
	(fd_state_machine::on_listen): New.
	(fd_state_machine::on_accept): New.
	(fd_state_machine::on_connect): New.
	(fd_state_machine::can_purge_p): Don't purge socket values.
	(get_fd_state): New.
	(region_model::mark_as_valid_fd): Use get_fd_state.
	(region_model::on_socket): New.
	(region_model::on_bind): New.
	(region_model::on_listen): New.
	(region_model::on_accept): New.
	(region_model::on_connect): New.
	* sm-fd.dot: Update to reflect sm-fd.cc changes.

gcc/ChangeLog:
	PR analyzer/106140
	* doc/gcc/gcc-command-options/option-summary.rst:
	Add -Wanalyzer-fd-phase-mismatch and -Wanalyzer-fd-type-mismatch.
	* doc/gcc/gcc-command-options/options-that-control-static-analysis.rst:
	Likewise.

gcc/testsuite/ChangeLog:
	PR analyzer/106140
	* gcc.dg/analyzer/fd-accept.c: New test.
	* gcc.dg/analyzer/fd-bind.c: New test.
	* gcc.dg/analyzer/fd-connect.c: New test.
	* gcc.dg/analyzer/fd-datagram-socket.c: New test.
	* gcc.dg/analyzer/fd-glibc-byte-stream-connection-server.c: New test.
	* gcc.dg/analyzer/fd-glibc-byte-stream-socket.c: New test.
	* gcc.dg/analyzer/fd-glibc-datagram-client.c: New test.
	* gcc.dg/analyzer/fd-glibc-datagram-socket.c: New test.
	* gcc.dg/analyzer/fd-glibc-make_named_socket.h: New test.
	* gcc.dg/analyzer/fd-listen.c: New test.
	* gcc.dg/analyzer/fd-manpage-getaddrinfo-client.c: New test.
	* gcc.dg/analyzer/fd-mappage-getaddrinfo-server.c: New test.
	* gcc.dg/analyzer/fd-socket-meaning.c: New test.
	* gcc.dg/analyzer/fd-socket-misuse.c: New test.
	* gcc.dg/analyzer/fd-stream-socket-active-open.c: New test.
	* gcc.dg/analyzer/fd-stream-socket-passive-open.c: New test.
	* gcc.dg/analyzer/fd-stream-socket.c: New test.
	* gcc.dg/analyzer/fd-symbolic-socket.c: New test.
	* gcc.dg/analyzer/pr104369-1.c: Add -Wno-analyzer-too-complex and
	-Wno-analyzer-fd-leak to options.
	* gcc.dg/analyzer/pr104369-2.c: Add -Wno-analyzer-fd-leak to
	options.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/analyzer/analyzer-language.cc             |    2 +
 gcc/analyzer/analyzer.opt                     |    8 +
 gcc/analyzer/engine.cc                        |   60 +-
 gcc/analyzer/exploded-graph.h                 |   10 +-
 gcc/analyzer/region-model-impl-calls.cc       |  150 +++
 gcc/analyzer/region-model.cc                  |   30 +
 gcc/analyzer/region-model.h                   |   57 +-
 gcc/analyzer/sm-fd.cc                         | 1110 ++++++++++++++++-
 gcc/analyzer/sm-fd.dot                        |   64 +
 .../gcc-command-options/option-summary.rst    |    2 +
 .../options-that-control-static-analysis.rst  |   38 +
 gcc/testsuite/gcc.dg/analyzer/fd-accept.c     |   69 +
 gcc/testsuite/gcc.dg/analyzer/fd-bind.c       |   74 ++
 gcc/testsuite/gcc.dg/analyzer/fd-connect.c    |   46 +
 .../gcc.dg/analyzer/fd-datagram-socket.c      |  108 ++
 .../fd-glibc-byte-stream-connection-server.c  |  133 ++
 .../analyzer/fd-glibc-byte-stream-socket.c    |   62 +
 .../analyzer/fd-glibc-datagram-client.c       |   56 +
 .../analyzer/fd-glibc-datagram-socket.c       |   52 +
 .../analyzer/fd-glibc-make_named_socket.h     |   47 +
 gcc/testsuite/gcc.dg/analyzer/fd-listen.c     |   63 +
 .../analyzer/fd-manpage-getaddrinfo-client.c  |  122 ++
 .../analyzer/fd-mappage-getaddrinfo-server.c  |  119 ++
 .../gcc.dg/analyzer/fd-socket-meaning.c       |   21 +
 .../gcc.dg/analyzer/fd-socket-misuse.c        |   98 ++
 .../analyzer/fd-stream-socket-active-open.c   |   74 ++
 .../analyzer/fd-stream-socket-passive-open.c  |  197 +++
 .../gcc.dg/analyzer/fd-stream-socket.c        |   98 ++
 .../gcc.dg/analyzer/fd-symbolic-socket.c      |   98 ++
 gcc/testsuite/gcc.dg/analyzer/pr104369-1.c    |    4 +-
 gcc/testsuite/gcc.dg/analyzer/pr104369-2.c    |    3 +
 31 files changed, 3015 insertions(+), 60 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-accept.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-bind.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-connect.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-datagram-socket.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-connection-server.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-socket.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-client.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-socket.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-glibc-make_named_socket.h
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-listen.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-manpage-getaddrinfo-client.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-mappage-getaddrinfo-server.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-socket-meaning.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-socket-misuse.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-active-open.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-passive-open.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-stream-socket.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-symbolic-socket.c
  

Comments

David Malcolm Nov. 15, 2022, 7:02 p.m. UTC | #1
On Fri, 2022-11-11 at 22:27 -0500, David Malcolm wrote:
> Changed in v2: ported doc changes from texinfo to sphinx
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> 
> I can self-approve this patch, but it depends on the named constants
> patch here:
>   * [PATCH v2] c, analyzer: support named constants in analyzer
> [PR106302]
>     *
> https://gcc.gnu.org/pipermail/gcc-patches/2022-November/605835.html
> which requires review of the C frontend changes.

Marek approved v3 of the named constants patch (thanks!), so I've now
committed both that, and this (ported back to texinfo from sphinx) to
trunk, as:
  r13-4073-gd8aba860b34203
and
  r13-4074-g86a90006864840
respectively.

Dave
  

Patch

diff --git a/gcc/analyzer/analyzer-language.cc b/gcc/analyzer/analyzer-language.cc
index ba4352b729a..0629b681e7c 100644
--- a/gcc/analyzer/analyzer-language.cc
+++ b/gcc/analyzer/analyzer-language.cc
@@ -72,6 +72,8 @@  on_finish_translation_unit (const translation_unit &tu)
   maybe_stash_named_constant (tu, "O_ACCMODE");
   maybe_stash_named_constant (tu, "O_RDONLY");
   maybe_stash_named_constant (tu, "O_WRONLY");
+  maybe_stash_named_constant (tu, "SOCK_STREAM");
+  maybe_stash_named_constant (tu, "SOCK_DGRAM");
 }
 
 /* Lookup NAME in the named constants stashed when the frontend TU finished.
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 518a5d422ff..95f345687d6 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -90,6 +90,14 @@  Wanalyzer-fd-leak
 Common Var(warn_analyzer_fd_leak) Init(1) Warning
 Warn about code paths in which a file descriptor is not closed.
 
+Wanalyzer-fd-phase-mismatch
+Common Var(warn_analyzer_fd_phase_mismatch) Init(1) Warning
+Warn about code paths in which an operation is attempted in the wrong phase of a file descriptor's lifetime.
+
+Wanalyzer-fd-type-mismatch
+Common Var(warn_analyzer_fd_type_mismatch) Init(1) Warning
+Warn about code paths in which an operation is attempted on the wrong type of file descriptor.
+
 Wanalyzer-fd-use-after-close
 Common Var(warn_analyzer_fd_use_after_close) Init(1) Warning
 Warn about code paths in which a read or write is performed on a closed file descriptor.
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 891be7c5c90..3ef411cae93 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -206,25 +206,6 @@  impl_region_model_context::terminate_path ()
     return m_path_ctxt->terminate_path ();
 }
 
-bool
-impl_region_model_context::get_state_map_by_name (const char *name,
-						  sm_state_map **out_smap,
-						  const state_machine **out_sm,
-						  unsigned *out_sm_idx)
-{
-  if (!m_new_state)
-    return false;
-
-  unsigned sm_idx;
-  if (!m_ext_state.get_sm_idx_by_name (name, &sm_idx))
-    return false;
-
-  *out_smap = m_new_state->m_checker_states[sm_idx];
-  *out_sm = &m_ext_state.get_sm (sm_idx);
-  *out_sm_idx = sm_idx;
-  return true;
-}
-
 /* struct setjmp_record.  */
 
 int
@@ -527,6 +508,47 @@  public:
   bool m_unknown_side_effects;
 };
 
+bool
+impl_region_model_context::
+get_state_map_by_name (const char *name,
+		       sm_state_map **out_smap,
+		       const state_machine **out_sm,
+		       unsigned *out_sm_idx,
+		       std::unique_ptr<sm_context> *out_sm_context)
+{
+  if (!m_new_state)
+    return false;
+
+  unsigned sm_idx;
+  if (!m_ext_state.get_sm_idx_by_name (name, &sm_idx))
+    return false;
+
+  const state_machine *sm = &m_ext_state.get_sm (sm_idx);
+  sm_state_map *new_smap = m_new_state->m_checker_states[sm_idx];
+
+  *out_smap = new_smap;
+  *out_sm = sm;
+  if (out_sm_idx)
+    *out_sm_idx = sm_idx;
+  if (out_sm_context)
+    {
+      const sm_state_map *old_smap = m_old_state->m_checker_states[sm_idx];
+      *out_sm_context
+	= make_unique<impl_sm_context> (*m_eg,
+					sm_idx,
+					*sm,
+					m_enode_for_diag,
+					m_old_state,
+					m_new_state,
+					old_smap,
+					new_smap,
+					m_path_ctxt,
+					m_stmt_finder,
+					false);
+    }
+  return true;
+}
+
 /* Subclass of stmt_finder for finding the best stmt to report the leak at,
    given the emission path.  */
 
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index a4cbc8f688a..86644c10835 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -98,10 +98,12 @@  class impl_region_model_context : public region_model_context
   {
     return &m_ext_state;
   }
-  bool get_state_map_by_name (const char *name,
-			      sm_state_map **out_smap,
-			      const state_machine **out_sm,
-			      unsigned *out_sm_idx) override;
+  bool
+  get_state_map_by_name (const char *name,
+			 sm_state_map **out_smap,
+			 const state_machine **out_sm,
+			 unsigned *out_sm_idx,
+			 std::unique_ptr<sm_context> *out_sm_context) override;
 
   const gimple *get_stmt () const override { return m_stmt; }
 
diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index a7134ed90bb..99597e0667a 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -407,6 +407,66 @@  region_model::impl_call_analyzer_get_unknown_ptr (const call_details &cd)
   cd.maybe_set_lhs (ptr_sval);
 }
 
+/* Handle the on_call_post part of "accept".  */
+
+void
+region_model::impl_call_accept (const call_details &cd)
+{
+  class outcome_of_accept : public succeed_or_fail_call_info
+  {
+  public:
+    outcome_of_accept (const call_details &cd, bool success)
+    : succeed_or_fail_call_info (cd, success)
+    {}
+
+    bool update_model (region_model *model,
+		       const exploded_edge *,
+		       region_model_context *ctxt) const final override
+    {
+      const call_details cd (get_call_details (model, ctxt));
+      return cd.get_model ()->on_accept (cd, m_success);
+    }
+  };
+
+  /* Body of region_model::impl_call_accept.  */
+  if (cd.get_ctxt ())
+    {
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_accept> (cd, false));
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_accept> (cd, true));
+      cd.get_ctxt ()->terminate_path ();
+    }
+}
+
+/* Handle the on_call_post part of "bind".  */
+
+void
+region_model::impl_call_bind (const call_details &cd)
+{
+  class outcome_of_bind : public succeed_or_fail_call_info
+  {
+  public:
+    outcome_of_bind (const call_details &cd, bool success)
+    : succeed_or_fail_call_info (cd, success)
+    {}
+
+    bool update_model (region_model *model,
+		       const exploded_edge *,
+		       region_model_context *ctxt) const final override
+    {
+      const call_details cd (get_call_details (model, ctxt));
+      return cd.get_model ()->on_bind (cd, m_success);
+    }
+  };
+
+  /* Body of region_model::impl_call_bind.  */
+  if (cd.get_ctxt ())
+    {
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_bind> (cd, false));
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_bind> (cd, true));
+      cd.get_ctxt ()->terminate_path ();
+    }
+}
+
 /* Handle the on_call_pre part of "__builtin_expect" etc.  */
 
 void
@@ -441,6 +501,36 @@  region_model::impl_call_calloc (const call_details &cd)
     }
 }
 
+/* Handle the on_call_post part of "connect".  */
+
+void
+region_model::impl_call_connect (const call_details &cd)
+{
+  class outcome_of_connect : public succeed_or_fail_call_info
+  {
+  public:
+    outcome_of_connect (const call_details &cd, bool success)
+    : succeed_or_fail_call_info (cd, success)
+    {}
+
+    bool update_model (region_model *model,
+		       const exploded_edge *,
+		       region_model_context *ctxt) const final override
+    {
+      const call_details cd (get_call_details (model, ctxt));
+      return cd.get_model ()->on_connect (cd, m_success);
+    }
+  };
+
+  /* Body of region_model::impl_call_connect.  */
+  if (cd.get_ctxt ())
+    {
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_connect> (cd, false));
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_connect> (cd, true));
+      cd.get_ctxt ()->terminate_path ();
+    }
+}
+
 /* Handle the on_call_pre part of "__errno_location".  */
 
 void
@@ -543,6 +633,36 @@  region_model::impl_call_free (const call_details &cd)
     }
 }
 
+/* Handle the on_call_post part of "listen".  */
+
+void
+region_model::impl_call_listen (const call_details &cd)
+{
+  class outcome_of_listen : public succeed_or_fail_call_info
+  {
+  public:
+    outcome_of_listen (const call_details &cd, bool success)
+    : succeed_or_fail_call_info (cd, success)
+    {}
+
+    bool update_model (region_model *model,
+		       const exploded_edge *,
+		       region_model_context *ctxt) const final override
+    {
+      const call_details cd (get_call_details (model, ctxt));
+      return cd.get_model ()->on_listen (cd, m_success);
+    }
+  };
+
+  /* Body of region_model::impl_call_listen.  */
+  if (cd.get_ctxt ())
+    {
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_listen> (cd, false));
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_listen> (cd, true));
+      cd.get_ctxt ()->terminate_path ();
+    }
+}
+
 /* Handle the on_call_pre part of "malloc".  */
 
 void
@@ -1055,6 +1175,36 @@  region_model::impl_call_realloc (const call_details &cd)
     }
 }
 
+/* Handle the on_call_post part of "socket".  */
+
+void
+region_model::impl_call_socket (const call_details &cd)
+{
+  class outcome_of_socket : public succeed_or_fail_call_info
+  {
+  public:
+    outcome_of_socket (const call_details &cd, bool success)
+    : succeed_or_fail_call_info (cd, success)
+    {}
+
+    bool update_model (region_model *model,
+		       const exploded_edge *,
+		       region_model_context *ctxt) const final override
+    {
+      const call_details cd (get_call_details (model, ctxt));
+      return cd.get_model ()->on_socket (cd, m_success);
+    }
+  };
+
+  /* Body of region_model::impl_call_socket.  */
+  if (cd.get_ctxt ())
+    {
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_socket> (cd, false));
+      cd.get_ctxt ()->bifurcate (make_unique<outcome_of_socket> (cd, true));
+      cd.get_ctxt ()->terminate_path ();
+    }
+}
+
 /* Handle the on_call_post part of "strchr" and "__builtin_strchr".  */
 
 void
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 5bae3cf5cd4..5f1dd0112d1 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -2293,6 +2293,11 @@  region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
 	  impl_call_realloc (cd);
 	  return false;
 	}
+      else if (is_named_call_p (callee_fndecl, "bind", call, 3))
+	{
+	  /* Handle in "on_call_post".  */
+	  return false;
+	}
       else if (is_named_call_p (callee_fndecl, "__errno_location", call, 0))
 	{
 	  impl_call_errno_location (cd);
@@ -2422,12 +2427,37 @@  region_model::on_call_post (const gcall *call,
 	  impl_call_operator_delete (cd);
 	  return;
 	}
+      else if (is_named_call_p (callee_fndecl, "accept", call, 3))
+	{
+	  impl_call_accept (cd);
+	  return;
+	}
+      else if (is_named_call_p (callee_fndecl, "bind", call, 3))
+	{
+	  impl_call_bind (cd);
+	  return;
+	}
+      else if (is_named_call_p (callee_fndecl, "connect", call, 3))
+	{
+	  impl_call_connect (cd);
+	  return;
+	}
+      else if (is_named_call_p (callee_fndecl, "listen", call, 2))
+	{
+	  impl_call_listen (cd);
+	  return;
+	}
       else if (is_pipe_call_p (callee_fndecl, "pipe", call, 1)
 	       || is_pipe_call_p (callee_fndecl, "pipe2", call, 2))
 	{
 	  impl_call_pipe (cd);
 	  return;
 	}
+      else if (is_named_call_p (callee_fndecl, "socket", call, 3))
+	{
+	  impl_call_socket (cd);
+	  return;
+	}
       else if (is_named_call_p (callee_fndecl, "strchr", call, 2)
 	       && POINTER_TYPE_P (cd.get_arg_type (0)))
 	{
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index bd81e6b6b9d..1e72c551dfa 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -338,6 +338,7 @@  class region_model
   void purge_state_involving (const svalue *sval, region_model_context *ctxt);
 
   /* Specific handling for on_call_pre.  */
+  void impl_call_accept (const call_details &cd);
   void impl_call_alloca (const call_details &cd);
   void impl_call_analyzer_describe (const gcall *call,
 				    region_model_context *ctxt);
@@ -349,20 +350,24 @@  class region_model
   void impl_call_analyzer_eval (const gcall *call,
 				region_model_context *ctxt);
   void impl_call_analyzer_get_unknown_ptr (const call_details &cd);
+  void impl_call_bind (const call_details &cd);
   void impl_call_builtin_expect (const call_details &cd);
   void impl_call_calloc (const call_details &cd);
+  void impl_call_connect (const call_details &cd);
   void impl_call_errno_location (const call_details &cd);
   bool impl_call_error (const call_details &cd, unsigned min_args,
 			bool *out_terminate_path);
   void impl_call_fgets (const call_details &cd);
   void impl_call_fread (const call_details &cd);
   void impl_call_free (const call_details &cd);
+  void impl_call_listen (const call_details &cd);
   void impl_call_malloc (const call_details &cd);
   void impl_call_memcpy (const call_details &cd);
   void impl_call_memset (const call_details &cd);
   void impl_call_pipe (const call_details &cd);
   void impl_call_putenv (const call_details &cd);
   void impl_call_realloc (const call_details &cd);
+  void impl_call_socket (const call_details &cd);
   void impl_call_strchr (const call_details &cd);
   void impl_call_strcpy (const call_details &cd);
   void impl_call_strlen (const call_details &cd);
@@ -548,6 +553,11 @@  class region_model
 
   /* Implemented in sm-fd.cc  */
   void mark_as_valid_fd (const svalue *sval, region_model_context *ctxt);
+  bool on_socket (const call_details &cd, bool successful);
+  bool on_bind (const call_details &cd, bool successful);
+  bool on_listen (const call_details &cd, bool successful);
+  bool on_accept (const call_details &cd, bool successful);
+  bool on_connect (const call_details &cd, bool successful);
 
   /* Implemented in sm-malloc.cc  */
   void on_realloc_with_move (const call_details &cd,
@@ -558,7 +568,16 @@  class region_model
   void mark_as_tainted (const svalue *sval,
 			region_model_context *ctxt);
 
- private:
+  bool add_constraint (const svalue *lhs,
+		       enum tree_code op,
+		       const svalue *rhs,
+		       region_model_context *ctxt);
+
+  const svalue *check_for_poison (const svalue *sval,
+				  tree expr,
+				  region_model_context *ctxt) const;
+
+private:
   const region *get_lvalue_1 (path_var pv, region_model_context *ctxt) const;
   const svalue *get_rvalue_1 (path_var pv, region_model_context *ctxt) const;
 
@@ -571,10 +590,6 @@  class region_model
 
   const known_function *get_known_function (tree fndecl) const;
 
-  bool add_constraint (const svalue *lhs,
-		       enum tree_code op,
-		       const svalue *rhs,
-		       region_model_context *ctxt);
   bool add_constraints_from_binop (const svalue *outer_lhs,
 				   enum tree_code outer_op,
 				   const svalue *outer_rhs,
@@ -605,9 +620,6 @@  class region_model
   bool called_from_main_p () const;
   const svalue *get_initial_value_for_global (const region *reg) const;
 
-  const svalue *check_for_poison (const svalue *sval,
-				  tree expr,
-				  region_model_context *ctxt) const;
   const region * get_region_for_poisoned_expr (tree expr) const;
 
   void check_dynamic_size_for_taint (enum memory_space mem_space,
@@ -744,30 +756,33 @@  class region_model_context
 
   /* Hook for clients to access the a specific state machine in
      any underlying program_state.  */
-  virtual bool get_state_map_by_name (const char *name,
-				      sm_state_map **out_smap,
-				      const state_machine **out_sm,
-				      unsigned *out_sm_idx) = 0;
+  virtual bool
+  get_state_map_by_name (const char *name,
+			 sm_state_map **out_smap,
+			 const state_machine **out_sm,
+			 unsigned *out_sm_idx,
+			 std::unique_ptr<sm_context> *out_sm_context) = 0;
 
   /* Precanned ways for clients to access specific state machines.  */
   bool get_fd_map (sm_state_map **out_smap,
 		   const state_machine **out_sm,
-		   unsigned *out_sm_idx)
+		   unsigned *out_sm_idx,
+		   std::unique_ptr<sm_context> *out_sm_context)
   {
     return get_state_map_by_name ("file-descriptor", out_smap, out_sm,
-				  out_sm_idx);
+				  out_sm_idx, out_sm_context);
   }
   bool get_malloc_map (sm_state_map **out_smap,
 		       const state_machine **out_sm,
 		       unsigned *out_sm_idx)
   {
-    return get_state_map_by_name ("malloc", out_smap, out_sm, out_sm_idx);
+    return get_state_map_by_name ("malloc", out_smap, out_sm, out_sm_idx, NULL);
   }
   bool get_taint_map (sm_state_map **out_smap,
 		      const state_machine **out_sm,
 		      unsigned *out_sm_idx)
   {
-    return get_state_map_by_name ("taint", out_smap, out_sm, out_sm_idx);
+    return get_state_map_by_name ("taint", out_smap, out_sm, out_sm_idx, NULL);
   }
 
   /* Get the current statement, if any.  */
@@ -819,7 +834,8 @@  public:
   bool get_state_map_by_name (const char *,
 			      sm_state_map **,
 			      const state_machine **,
-			      unsigned *) override
+			      unsigned *,
+			      std::unique_ptr<sm_context> *) override
   {
     return false;
   }
@@ -946,9 +962,12 @@  class region_model_context_decorator : public region_model_context
   bool get_state_map_by_name (const char *name,
 			      sm_state_map **out_smap,
 			      const state_machine **out_sm,
-			      unsigned *out_sm_idx) override
+			      unsigned *out_sm_idx,
+			      std::unique_ptr<sm_context> *out_sm_context)
+    override
   {
-    return m_inner->get_state_map_by_name (name, out_smap, out_sm, out_sm_idx);
+    return m_inner->get_state_map_by_name (name, out_smap, out_sm, out_sm_idx,
+					   out_sm_context);
   }
 
   const gimple *get_stmt () const override
diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc
index 370115d56bf..d0b587143d0 100644
--- a/gcc/analyzer/sm-fd.cc
+++ b/gcc/analyzer/sm-fd.cc
@@ -45,6 +45,8 @@  along with GCC; see the file COPYING3.  If not see
 #include "analyzer/region-model.h"
 #include "bitmap.h"
 #include "analyzer/program-state.h"
+#include "analyzer/supergraph.h"
+#include "analyzer/analyzer-language.h"
 
 #if ENABLE_ANALYZER
 
@@ -76,6 +78,17 @@  enum dup
   DUP_3
 };
 
+/* Enum for use by -Wanalyzer-fd-phase-mismatch.  */
+
+enum expected_phase
+{
+  EXPECTED_PHASE_CAN_TRANSFER, /* can "read"/"write".  */
+  EXPECTED_PHASE_CAN_BIND,
+  EXPECTED_PHASE_CAN_LISTEN,
+  EXPECTED_PHASE_CAN_ACCEPT,
+  EXPECTED_PHASE_CAN_CONNECT
+};
+
 class fd_state_machine : public state_machine
 {
 public:
@@ -116,6 +129,9 @@  public:
 
   bool is_unchecked_fd_p (state_t s) const;
   bool is_valid_fd_p (state_t s) const;
+  bool is_socket_fd_p (state_t s) const;
+  bool is_datagram_socket_fd_p (state_t s) const;
+  bool is_stream_socket_fd_p (state_t s) const;
   bool is_closed_fd_p (state_t s) const;
   bool is_constant_fd_p (state_t s) const;
   bool is_readonly_fd_p (state_t s) const;
@@ -130,6 +146,27 @@  public:
 			 const svalue *fd_sval,
 			 const extrinsic_state &ext_state) const;
 
+  bool on_socket (const call_details &cd,
+		  bool successful,
+		  sm_context *sm_ctxt,
+		  const extrinsic_state &ext_state) const;
+  bool on_bind (const call_details &cd,
+		bool successful,
+		sm_context *sm_ctxt,
+		const extrinsic_state &ext_state) const;
+  bool on_listen (const call_details &cd,
+		  bool successful,
+		  sm_context *sm_ctxt,
+		  const extrinsic_state &ext_state) const;
+  bool on_accept (const call_details &cd,
+		  bool successful,
+		  sm_context *sm_ctxt,
+		  const extrinsic_state &ext_state) const;
+  bool on_connect (const call_details &cd,
+		   bool successful,
+		   sm_context *sm_ctxt,
+		   const extrinsic_state &ext_state) const;
+
   /* State for a constant file descriptor (>= 0) */
   state_t m_constant_fd;
 
@@ -156,6 +193,29 @@  public:
   /* State for a file descriptor that has been closed.  */
   state_t m_closed;
 
+  /* States for FDs relating to socket APIs.  */
+
+  /* Result of successful "socket" with SOCK_DGRAM.  */
+  state_t m_new_datagram_socket;
+  /* Result of successful "socket" with SOCK_STREAM.  */
+  state_t m_new_stream_socket;
+  /* Result of successful "socket" with unknown type.  */
+  state_t m_new_unknown_socket;
+
+  /* The above after a successful call to "bind".  */
+  state_t m_bound_datagram_socket;
+  state_t m_bound_stream_socket;
+  state_t m_bound_unknown_socket;
+
+  /* A bound socket after a successful call to "listen" (stream or unknown).  */
+  state_t m_listening_stream_socket;
+
+  /* (i) the new FD as a result of a succesful call to "accept" on a
+     listening socket (via a passive open), or
+     (ii) an active socket after a successful call to "connect"
+     (via an active open).  */
+  state_t m_connected_stream_socket;
+
   /* State for a file descriptor that we do not want to track anymore . */
   state_t m_stop;
 
@@ -163,6 +223,8 @@  public:
   tree m_O_ACCMODE;
   tree m_O_RDONLY;
   tree m_O_WRONLY;
+  tree m_SOCK_STREAM;
+  tree m_SOCK_DGRAM;
 
 private:
   void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
@@ -195,6 +257,23 @@  private:
   void check_for_dup (sm_context *sm_ctxt, const supernode *node,
        const gimple *stmt, const gcall *call, const tree callee_fndecl,
        enum dup kind) const;
+
+  state_t get_state_for_socket_type (const svalue *socket_type_sval) const;
+
+  bool check_for_socket_fd (const call_details &cd,
+			    bool successful,
+			    sm_context *sm_ctxt,
+			    const svalue *fd_sval,
+			    const supernode *node,
+			    state_t old_state,
+			    bool *complained = NULL) const;
+  bool check_for_new_socket_fd (const call_details &cd,
+				bool successful,
+				sm_context *sm_ctxt,
+				const svalue *fd_sval,
+				const supernode *node,
+				state_t old_state,
+				enum expected_phase expected_phase) const;
 };
 
 /* Base diagnostic class relative to fd_state_machine.  */
@@ -214,9 +293,7 @@  public:
   label_text
   describe_state_change (const evdesc::state_change &change) override
   {
-    if (change.m_old_state == m_sm.get_start_state ()
-	&& (m_sm.is_unchecked_fd_p (change.m_new_state)
-	    || m_sm.is_valid_fd_p (change.m_new_state)))
+    if (change.m_old_state == m_sm.get_start_state ())
       {
 	if (change.m_new_state == m_sm.m_unchecked_read_write
 	    || change.m_new_state == m_sm.m_valid_read_write)
@@ -229,8 +306,32 @@  public:
 	if (change.m_new_state == m_sm.m_unchecked_write_only
 	    || change.m_new_state == m_sm.m_valid_write_only)
 	  return change.formatted_print ("opened here as write-only");
+
+	if (change.m_new_state == m_sm.m_new_datagram_socket)
+	  return change.formatted_print ("datagram socket created here");
+
+	if (change.m_new_state == m_sm.m_new_stream_socket)
+	  return change.formatted_print ("stream socket created here");
+
+	if (change.m_new_state == m_sm.m_new_unknown_socket
+	    || change.m_new_state == m_sm.m_connected_stream_socket)
+	  return change.formatted_print ("socket created here");
       }
 
+    if (change.m_new_state == m_sm.m_bound_datagram_socket)
+      return change.formatted_print ("datagram socket bound here");
+
+    if (change.m_new_state == m_sm.m_bound_stream_socket)
+      return change.formatted_print ("stream socket bound here");
+
+    if (change.m_new_state == m_sm.m_bound_unknown_socket
+	|| change.m_new_state == m_sm.m_connected_stream_socket)
+	  return change.formatted_print ("socket bound here");
+
+    if (change.m_new_state == m_sm.m_listening_stream_socket)
+      return change.formatted_print
+	("stream socket marked as passive here via %qs", "listen");
+
     if (change.m_new_state == m_sm.m_closed)
       return change.formatted_print ("closed here");
 
@@ -263,7 +364,10 @@  public:
       const evdesc::state_change &change) const final override
   {
     if (change.m_old_state == m_sm.get_start_state ()
-		&& (m_sm.is_unchecked_fd_p (change.m_new_state)))
+	&& (m_sm.is_unchecked_fd_p (change.m_new_state)
+	    || change.m_new_state == m_sm.m_new_datagram_socket
+	    || change.m_new_state == m_sm.m_new_stream_socket
+	    || change.m_new_state == m_sm.m_new_unknown_socket))
       return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
 			 diagnostic_event::NOUN_resource);
     if (change.m_new_state == m_sm.m_closed)
@@ -680,6 +784,289 @@  private:
   diagnostic_event_id_t m_first_open_event;
 };
 
+/* Concrete pending_diagnostic subclass for -Wanalyzer-fd-phase-mismatch.  */
+
+class fd_phase_mismatch : public fd_param_diagnostic
+{
+public:
+  fd_phase_mismatch (const fd_state_machine &sm, tree arg,
+		     const tree callee_fndecl,
+		     state_machine::state_t actual_state,
+		     enum expected_phase expected_phase)
+  : fd_param_diagnostic (sm, arg, callee_fndecl),
+    m_actual_state (actual_state),
+    m_expected_phase (expected_phase)
+  {
+    gcc_assert (m_sm.is_socket_fd_p (actual_state));
+    switch (expected_phase)
+      {
+      case EXPECTED_PHASE_CAN_TRANSFER:
+	gcc_assert (actual_state == m_sm.m_new_stream_socket
+		    || actual_state == m_sm.m_bound_stream_socket
+		    || actual_state == m_sm.m_listening_stream_socket);
+	break;
+      case EXPECTED_PHASE_CAN_BIND:
+	gcc_assert (actual_state == m_sm.m_bound_datagram_socket
+		    || actual_state == m_sm.m_bound_stream_socket
+		    || actual_state == m_sm.m_bound_unknown_socket
+		    || actual_state == m_sm.m_connected_stream_socket
+		    || actual_state == m_sm.m_listening_stream_socket);
+	break;
+      case EXPECTED_PHASE_CAN_LISTEN:
+	gcc_assert (actual_state == m_sm.m_new_stream_socket
+		    || actual_state == m_sm.m_new_unknown_socket
+		    || actual_state == m_sm.m_connected_stream_socket);
+	break;
+      case EXPECTED_PHASE_CAN_ACCEPT:
+	gcc_assert (actual_state == m_sm.m_new_stream_socket
+		    || actual_state == m_sm.m_new_unknown_socket
+		    || actual_state == m_sm.m_bound_stream_socket
+		    || actual_state == m_sm.m_bound_unknown_socket
+		    || actual_state == m_sm.m_connected_stream_socket);
+	break;
+      case EXPECTED_PHASE_CAN_CONNECT:
+	gcc_assert (actual_state == m_sm.m_bound_datagram_socket
+		    || actual_state == m_sm.m_bound_stream_socket
+		    || actual_state == m_sm.m_bound_unknown_socket
+		    || actual_state == m_sm.m_listening_stream_socket
+		    || actual_state == m_sm.m_connected_stream_socket);
+	break;
+      }
+  }
+
+  const char *
+  get_kind () const final override
+  {
+    return "fd_phase_mismatch";
+  }
+
+  bool
+  subclass_equal_p (const pending_diagnostic &base_other) const final override
+  {
+    const fd_phase_mismatch &sub_other = (const fd_phase_mismatch &)base_other;
+    if (!fd_param_diagnostic ::subclass_equal_p (sub_other))
+      return false;
+    return (m_actual_state == sub_other.m_actual_state
+	    && m_expected_phase == sub_other.m_expected_phase);
+  }
+
+  int
+  get_controlling_option () const final override
+  {
+    return OPT_Wanalyzer_fd_phase_mismatch;
+  }
+
+  bool
+  emit (rich_location *rich_loc) final override
+  {
+    /* CWE-666: Operation on Resource in Wrong Phase of Lifetime.  */
+    diagnostic_metadata m;
+    m.add_cwe (666);
+    return warning_at (rich_loc, get_controlling_option (),
+		       "%qE on file descriptor %qE in wrong phase",
+		       m_callee_fndecl, m_arg);
+  }
+
+  label_text
+  describe_final_event (const evdesc::final_event &ev) final override
+  {
+    switch (m_expected_phase)
+      {
+      case EXPECTED_PHASE_CAN_TRANSFER:
+	{
+	  if (m_actual_state == m_sm.m_new_stream_socket)
+	    return ev.formatted_print
+	      ("%qE expects a stream socket to be connected via %qs"
+	       " but %qE has not yet been bound",
+	       m_callee_fndecl, "accept", m_arg);
+	  if (m_actual_state == m_sm.m_bound_stream_socket)
+	    return ev.formatted_print
+	      ("%qE expects a stream socket to be connected via %qs"
+	       " but %qE is not yet listening",
+	       m_callee_fndecl, "accept", m_arg);
+	  if (m_actual_state == m_sm.m_listening_stream_socket)
+	    return ev.formatted_print
+	      ("%qE expects a stream socket to be connected via"
+	       " the return value of %qs"
+	       " but %qE is listening; wrong file descriptor?",
+	       m_callee_fndecl, "accept", m_arg);
+	}
+	break;
+      case EXPECTED_PHASE_CAN_BIND:
+	{
+	  if (m_actual_state == m_sm.m_bound_datagram_socket
+	      || m_actual_state == m_sm.m_bound_stream_socket
+	      || m_actual_state == m_sm.m_bound_unknown_socket)
+	    return ev.formatted_print
+	      ("%qE expects a new socket file descriptor"
+	       " but %qE has already been bound",
+	       m_callee_fndecl, m_arg);
+	  if (m_actual_state == m_sm.m_connected_stream_socket)
+	    return ev.formatted_print
+	      ("%qE expects a new socket file descriptor"
+	       " but %qE is already connected",
+	       m_callee_fndecl, m_arg);
+	  if (m_actual_state == m_sm.m_listening_stream_socket)
+	    return ev.formatted_print
+	      ("%qE expects a new socket file descriptor"
+	       " but %qE is already listening",
+	       m_callee_fndecl, m_arg);
+	}
+	break;
+      case EXPECTED_PHASE_CAN_LISTEN:
+	{
+	  if (m_actual_state == m_sm.m_new_stream_socket
+	      || m_actual_state == m_sm.m_new_unknown_socket)
+	    return ev.formatted_print
+	      ("%qE expects a bound stream socket file descriptor"
+	       " but %qE has not yet been bound",
+	       m_callee_fndecl, m_arg);
+	  if (m_actual_state == m_sm.m_connected_stream_socket)
+	    return ev.formatted_print
+	      ("%qE expects a bound stream socket file descriptor"
+	       " but %qE is connected",
+	       m_callee_fndecl, m_arg);
+	}
+	break;
+      case EXPECTED_PHASE_CAN_ACCEPT:
+	{
+	  if (m_actual_state == m_sm.m_new_stream_socket
+	      || m_actual_state == m_sm.m_new_unknown_socket)
+	    return ev.formatted_print
+	      ("%qE expects a listening stream socket file descriptor"
+	       " but %qE has not yet been bound",
+	       m_callee_fndecl, m_arg);
+	  if (m_actual_state == m_sm.m_bound_stream_socket
+	      || m_actual_state == m_sm.m_bound_unknown_socket)
+	    return ev.formatted_print
+	      ("%qE expects a listening stream socket file descriptor"
+	       " whereas %qE is bound but not yet listening",
+	       m_callee_fndecl, m_arg);
+	  if (m_actual_state == m_sm.m_connected_stream_socket)
+	    return ev.formatted_print
+	      ("%qE expects a listening stream socket file descriptor"
+	       " but %qE is connected",
+	       m_callee_fndecl, m_arg);
+	}
+	break;
+      case EXPECTED_PHASE_CAN_CONNECT:
+	{
+	  if (m_actual_state == m_sm.m_bound_datagram_socket
+	      || m_actual_state == m_sm.m_bound_stream_socket
+	      || m_actual_state == m_sm.m_bound_unknown_socket)
+	    return ev.formatted_print
+	      ("%qE expects a new socket file descriptor but %qE is bound",
+	       m_callee_fndecl, m_arg);
+	  else
+	    return ev.formatted_print
+	      ("%qE expects a new socket file descriptor", m_callee_fndecl);
+	}
+	break;
+      }
+    gcc_unreachable ();
+  }
+
+private:
+  state_machine::state_t m_actual_state;
+  enum expected_phase m_expected_phase;
+};
+
+/* Enum for use by -Wanalyzer-fd-type-mismatch.  */
+
+enum expected_type
+{
+ EXPECTED_TYPE_SOCKET,
+ EXPECTED_TYPE_STREAM_SOCKET
+};
+
+/* Concrete pending_diagnostic subclass for -Wanalyzer-fd-type-mismatch.  */
+
+class fd_type_mismatch : public fd_param_diagnostic
+{
+public:
+  fd_type_mismatch (const fd_state_machine &sm, tree arg,
+		    const tree callee_fndecl,
+		    state_machine::state_t actual_state,
+		    enum expected_type expected_type)
+  : fd_param_diagnostic (sm, arg, callee_fndecl),
+    m_actual_state (actual_state),
+    m_expected_type (expected_type)
+  {
+  }
+
+  const char *
+  get_kind () const final override
+  {
+    return "fd_type_mismatch";
+  }
+
+  bool
+  subclass_equal_p (const pending_diagnostic &base_other) const final override
+  {
+    const fd_type_mismatch &sub_other = (const fd_type_mismatch &)base_other;
+    if (!fd_param_diagnostic ::subclass_equal_p (sub_other))
+      return false;
+    return (m_actual_state == sub_other.m_actual_state
+	    && m_expected_type == sub_other.m_expected_type);
+  }
+
+  int
+  get_controlling_option () const final override
+  {
+    return OPT_Wanalyzer_fd_type_mismatch;
+  }
+
+  bool
+  emit (rich_location *rich_loc) final override
+  {
+    switch (m_expected_type)
+      {
+      default:
+	gcc_unreachable ();
+      case EXPECTED_TYPE_SOCKET:
+	return warning_at (rich_loc, get_controlling_option (),
+			   "%qE on non-socket file descriptor %qE",
+			   m_callee_fndecl, m_arg);
+      case EXPECTED_TYPE_STREAM_SOCKET:
+	if (m_sm.is_datagram_socket_fd_p (m_actual_state))
+	  return warning_at (rich_loc, get_controlling_option (),
+			     "%qE on datagram socket file descriptor %qE",
+			     m_callee_fndecl, m_arg);
+	else
+	  return warning_at (rich_loc, get_controlling_option (),
+			     "%qE on non-stream-socket file descriptor %qE",
+			     m_callee_fndecl, m_arg);
+      }
+  }
+
+  label_text
+  describe_final_event (const evdesc::final_event &ev) final override
+  {
+    switch (m_expected_type)
+      {
+      default:
+	break;
+	gcc_unreachable ();
+      case EXPECTED_TYPE_SOCKET:
+      case EXPECTED_TYPE_STREAM_SOCKET:
+	if (!m_sm.is_socket_fd_p (m_actual_state))
+	  return ev.formatted_print ("%qE expects a socket file descriptor"
+				     " but %qE is not a socket",
+				     m_callee_fndecl, m_arg);
+      }
+    gcc_assert (m_expected_type == EXPECTED_TYPE_STREAM_SOCKET);
+    gcc_assert (m_sm.is_datagram_socket_fd_p (m_actual_state));
+    return ev.formatted_print
+      ("%qE expects a stream socket file descriptor"
+       " but %qE is a datagram socket",
+       m_callee_fndecl, m_arg);
+  }
+
+private:
+  state_machine::state_t m_actual_state;
+  enum expected_type m_expected_type;
+};
+
 fd_state_machine::fd_state_machine (logger *logger)
     : state_machine ("file-descriptor", logger),
       m_constant_fd (add_state ("fd-constant")),
@@ -691,10 +1078,20 @@  fd_state_machine::fd_state_machine (logger *logger)
       m_valid_write_only (add_state ("fd-valid-write-only")),
       m_invalid (add_state ("fd-invalid")),
       m_closed (add_state ("fd-closed")),
+      m_new_datagram_socket (add_state ("fd-new-datagram-socket")),
+      m_new_stream_socket (add_state ("fd-new-stream-socket")),
+      m_new_unknown_socket (add_state ("fd-new-unknown-socket")),
+      m_bound_datagram_socket (add_state ("fd-bound-datagram-socket")),
+      m_bound_stream_socket (add_state ("fd-bound-stream-socket")),
+      m_bound_unknown_socket (add_state ("fd-bound-unknown-socket")),
+      m_listening_stream_socket (add_state ("fd-listening-stream-socket")),
+      m_connected_stream_socket (add_state ("fd-connected-stream-socket")),
       m_stop (add_state ("fd-stop")),
       m_O_ACCMODE (get_stashed_constant_by_name ("O_ACCMODE")),
       m_O_RDONLY (get_stashed_constant_by_name ("O_RDONLY")),
-      m_O_WRONLY (get_stashed_constant_by_name ("O_WRONLY"))
+      m_O_WRONLY (get_stashed_constant_by_name ("O_WRONLY")),
+      m_SOCK_STREAM (get_stashed_constant_by_name ("SOCK_STREAM")),
+      m_SOCK_DGRAM (get_stashed_constant_by_name ("SOCK_DGRAM"))
 {
 }
 
@@ -714,6 +1111,39 @@  fd_state_machine::is_valid_fd_p (state_t s) const
        || s == m_valid_write_only);
 }
 
+bool
+fd_state_machine::is_socket_fd_p (state_t s) const
+{
+  return (s == m_new_datagram_socket
+	  || s == m_new_stream_socket
+	  || s == m_new_unknown_socket
+	  || s == m_bound_datagram_socket
+	  || s == m_bound_stream_socket
+	  || s == m_bound_unknown_socket
+	  || s == m_listening_stream_socket
+	  || s == m_connected_stream_socket);
+}
+
+bool
+fd_state_machine::is_datagram_socket_fd_p (state_t s) const
+{
+  return (s == m_new_datagram_socket
+	  || s == m_new_unknown_socket
+	  || s == m_bound_datagram_socket
+	  || s == m_bound_unknown_socket);
+}
+
+bool
+fd_state_machine::is_stream_socket_fd_p (state_t s) const
+{
+  return (s == m_new_stream_socket
+	  || s == m_new_unknown_socket
+	  || s == m_bound_stream_socket
+	  || s == m_bound_unknown_socket
+	  || s == m_listening_stream_socket
+	  || s == m_connected_stream_socket);
+}
+
 enum access_mode
 fd_state_machine::get_access_mode_from_flag (int flag) const
 {
@@ -1079,6 +1509,14 @@  fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node,
   sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed);
   sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed);
   sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed);
+  sm_ctxt->on_transition (node, stmt, arg, m_new_datagram_socket, m_closed);
+  sm_ctxt->on_transition (node, stmt, arg, m_new_stream_socket, m_closed);
+  sm_ctxt->on_transition (node, stmt, arg, m_new_unknown_socket, m_closed);
+  sm_ctxt->on_transition (node, stmt, arg, m_bound_datagram_socket, m_closed);
+  sm_ctxt->on_transition (node, stmt, arg, m_bound_stream_socket, m_closed);
+  sm_ctxt->on_transition (node, stmt, arg, m_bound_unknown_socket, m_closed);
+  sm_ctxt->on_transition (node, stmt, arg, m_listening_stream_socket, m_closed);
+  sm_ctxt->on_transition (node, stmt, arg, m_connected_stream_socket, m_closed);
 
   if (is_closed_fd_p (state))
     {
@@ -1121,7 +1559,22 @@  fd_state_machine::check_for_open_fd (
 
   else
     {
-      if (!(is_valid_fd_p (state) || state == m_start || state == m_stop))
+      if (state == m_new_stream_socket
+	  || state == m_bound_stream_socket
+	  || state == m_listening_stream_socket)
+	/* Complain about fncall on socket in wrong phase.  */
+	sm_ctxt->warn
+	  (node, stmt, arg,
+	   make_unique<fd_phase_mismatch> (*this, diag_arg,
+					   callee_fndecl,
+					   state,
+					   EXPECTED_PHASE_CAN_TRANSFER));
+      else if (!(is_valid_fd_p (state)
+		 || state == m_new_datagram_socket
+		 || state == m_bound_unknown_socket
+		 || state == m_connected_stream_socket
+		 || state == m_start
+		 || state == m_stop))
 	{
 	  if (!is_constant_fd_p (state))
 	    sm_ctxt->warn (
@@ -1157,6 +1610,529 @@  fd_state_machine::check_for_open_fd (
     }
 }
 
+static bool
+add_constraint_ge_zero (region_model *model,
+			const svalue *fd_sval,
+			region_model_context *ctxt)
+{
+  const svalue *zero
+    = model->get_manager ()->get_or_create_int_cst (integer_type_node, 0);
+  return model->add_constraint (fd_sval, GE_EXPR, zero, ctxt);
+}
+
+/* Get the state for a new socket type based on SOCKET_TYPE_SVAL,
+   a SOCK_* value.  */
+
+state_machine::state_t
+fd_state_machine::
+get_state_for_socket_type (const svalue *socket_type_sval) const
+{
+  if (tree socket_type_cst = socket_type_sval->maybe_get_constant ())
+    {
+      /* Attempt to use SOCK_* constants stashed from the frontend.  */
+      if (tree_int_cst_equal (socket_type_cst, m_SOCK_STREAM))
+	return m_new_stream_socket;
+      if (tree_int_cst_equal (socket_type_cst, m_SOCK_DGRAM))
+	return m_new_datagram_socket;
+    }
+
+  /* Unrecognized constant, or a symbolic "type" value.  */
+  return m_new_unknown_socket;
+}
+
+/* Update the model and fd state for an outcome of a call to "socket",
+   where SUCCESSFUL indicate which of the two outcomes.
+   Return true if the outcome is feasible, or false to reject it.  */
+
+bool
+fd_state_machine::on_socket (const call_details &cd,
+			     bool successful,
+			     sm_context *sm_ctxt,
+			     const extrinsic_state &ext_state) const
+{
+  const gcall *stmt = cd.get_call_stmt ();
+  engine *eng = ext_state.get_engine ();
+  const supergraph *sg = eng->get_supergraph ();
+  const supernode *node = sg->get_supernode_for_stmt (stmt);
+  region_model *model = cd.get_model ();
+
+  if (successful)
+    {
+      if (gimple_call_lhs (stmt))
+	{
+	  conjured_purge p (model, cd.get_ctxt ());
+	  region_model_manager *mgr = model->get_manager ();
+	  const svalue *new_fd
+	    = mgr->get_or_create_conjured_svalue (integer_type_node,
+						  stmt,
+						  cd.get_lhs_region (),
+						  p);
+	  if (!add_constraint_ge_zero (model, new_fd, cd.get_ctxt ()))
+	    return false;
+
+	  const svalue *socket_type_sval = cd.get_arg_svalue (1);
+	  state_machine::state_t new_state
+	    = get_state_for_socket_type (socket_type_sval);
+	  sm_ctxt->on_transition (node, stmt, new_fd, m_start, new_state);
+	  model->set_value (cd.get_lhs_region (), new_fd, cd.get_ctxt ());
+	}
+      else
+	sm_ctxt->warn (node, stmt, NULL_TREE,
+		       make_unique<fd_leak> (*this, NULL_TREE));
+    }
+  else
+    {
+      /* Return -1; set errno.  */
+      model->update_for_int_cst_return (cd, -1, true);
+      model->set_errno (cd);
+    }
+
+  return true;
+}
+
+/* Check that FD_SVAL is usable by socket APIs.
+   Complain if it has been closed, if it is a non-socket,
+   or is invalid.
+   If COMPLAINED is non-NULL and a problem is found,
+   write *COMPLAINED = true.
+
+   If SUCCESSFUL is true, attempt to add the constraint that FD_SVAL >= 0.
+   Return true if this outcome is feasible.  */
+
+bool
+fd_state_machine::check_for_socket_fd (const call_details &cd,
+				       bool successful,
+				       sm_context *sm_ctxt,
+				       const svalue *fd_sval,
+				       const supernode *node,
+				       state_t old_state,
+				       bool *complained) const
+{
+  const gcall *stmt = cd.get_call_stmt ();
+
+  if (is_closed_fd_p (old_state))
+    {
+      tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
+      sm_ctxt->warn
+	(node, stmt, fd_sval,
+	 make_unique<fd_use_after_close> (*this, diag_arg,
+					  cd.get_fndecl_for_call ()));
+      if (complained)
+	*complained = true;
+      if (successful)
+	return false;
+    }
+  else if (is_unchecked_fd_p (old_state) || is_valid_fd_p (old_state))
+    {
+      /* Complain about non-socket.  */
+      tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
+      sm_ctxt->warn
+	(node, stmt, fd_sval,
+	 make_unique<fd_type_mismatch> (*this, diag_arg,
+					cd.get_fndecl_for_call (),
+					old_state,
+					EXPECTED_TYPE_SOCKET));
+      if (complained)
+	*complained = true;
+      if (successful)
+	return false;
+    }
+  else if (old_state == m_invalid)
+    {
+      tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
+      sm_ctxt->warn
+	(node, stmt, fd_sval,
+	 make_unique<fd_use_without_check> (*this, diag_arg,
+					    cd.get_fndecl_for_call ()));
+      if (complained)
+	*complained = true;
+      if (successful)
+	return false;
+    }
+
+  if (successful)
+    if (!add_constraint_ge_zero (cd.get_model (), fd_sval, cd.get_ctxt ()))
+      return false;
+
+  return true;
+}
+
+/* For use by "bind" and "connect".
+   As per fd_state_machine::check_for_socket_fd above,
+   but also complain if we don't have a new socket, and check that
+   we can read up to the size bytes from the address.  */
+
+bool
+fd_state_machine::check_for_new_socket_fd (const call_details &cd,
+					   bool successful,
+					   sm_context *sm_ctxt,
+					   const svalue *fd_sval,
+					   const supernode *node,
+					   state_t old_state,
+					   enum expected_phase expected_phase)
+  const
+{
+  bool complained = false;
+
+  /* Check address and len.  */
+  const svalue *address_sval = cd.get_arg_svalue (1);
+  const svalue *len_sval = cd.get_arg_svalue (2);
+
+  /* Check that we can read the given number of bytes from the
+     address.  */
+  region_model *model = cd.get_model ();
+  const region *address_reg
+    = model->deref_rvalue (address_sval, cd.get_arg_tree (1),
+			   cd.get_ctxt ());
+  const region *sized_address_reg
+    = model->get_manager ()->get_sized_region (address_reg,
+					       NULL_TREE,
+					       len_sval);
+  model->get_store_value (sized_address_reg, cd.get_ctxt ());
+
+  if (!check_for_socket_fd (cd, successful, sm_ctxt,
+			    fd_sval, node, old_state, &complained))
+    return false;
+  else if (!complained
+	   && !(old_state == m_new_stream_socket
+		|| old_state == m_new_datagram_socket
+		|| old_state == m_new_unknown_socket
+		|| old_state == m_start
+		|| old_state == m_stop))
+    {
+      /* Complain about "bind" or "connect" in wrong phase.  */
+      tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
+      sm_ctxt->warn
+	(node, cd.get_call_stmt (), fd_sval,
+	 make_unique<fd_phase_mismatch> (*this, diag_arg,
+					 cd.get_fndecl_for_call (),
+					 old_state,
+					 expected_phase));
+      if (successful)
+	return false;
+    }
+  else if (!successful)
+    {
+      /* If we were in the start state, assume we had a new socket.  */
+      if (old_state == m_start)
+	sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval,
+				 m_new_unknown_socket);
+    }
+
+  /* Passing NULL as the address will lead to failure.  */
+  if (successful)
+    if (address_sval->all_zeroes_p ())
+      return false;
+
+  return true;
+}
+
+/* Update the model and fd state for an outcome of a call to "bind",
+   where SUCCESSFUL indicate which of the two outcomes.
+   Return true if the outcome is feasible, or false to reject it.  */
+
+bool
+fd_state_machine::on_bind (const call_details &cd,
+			   bool successful,
+			   sm_context *sm_ctxt,
+			   const extrinsic_state &ext_state) const
+{
+  const gcall *stmt = cd.get_call_stmt ();
+  engine *eng = ext_state.get_engine ();
+  const supergraph *sg = eng->get_supergraph ();
+  const supernode *node = sg->get_supernode_for_stmt (stmt);
+  const svalue *fd_sval = cd.get_arg_svalue (0);
+  region_model *model = cd.get_model ();
+  state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
+
+  if (!check_for_new_socket_fd (cd, successful, sm_ctxt,
+				fd_sval, node, old_state,
+				EXPECTED_PHASE_CAN_BIND))
+    return false;
+
+  if (successful)
+    {
+      state_t next_state = NULL;
+      if (old_state == m_new_stream_socket)
+	next_state = m_bound_stream_socket;
+      else if (old_state == m_new_datagram_socket)
+	next_state = m_bound_datagram_socket;
+      else if (old_state == m_new_unknown_socket)
+	next_state = m_bound_unknown_socket;
+      else if (old_state == m_start)
+	next_state = m_bound_unknown_socket;
+      else if (old_state == m_stop)
+	next_state = m_stop;
+      else
+	gcc_unreachable ();
+      sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval, next_state);
+      model->update_for_zero_return (cd, true);
+    }
+  else
+    {
+      /* Return -1; set errno.  */
+      model->update_for_int_cst_return (cd, -1, true);
+      model->set_errno (cd);
+    }
+
+  return true;
+}
+
+/* Update the model and fd state for an outcome of a call to "listen",
+   where SUCCESSFUL indicate which of the two outcomes.
+   Return true if the outcome is feasible, or false to reject it.  */
+
+bool
+fd_state_machine::on_listen (const call_details &cd,
+			     bool successful,
+			     sm_context *sm_ctxt,
+			     const extrinsic_state &ext_state) const
+{
+  const gcall *stmt = cd.get_call_stmt ();
+  engine *eng = ext_state.get_engine ();
+  const supergraph *sg = eng->get_supergraph ();
+  const supernode *node = sg->get_supernode_for_stmt (cd.get_call_stmt ());
+  const svalue *fd_sval = cd.get_arg_svalue (0);
+  region_model *model = cd.get_model ();
+  state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
+
+  /* We expect a stream socket that's had "bind" called on it.  */
+  if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state))
+    return false;
+  if (!(old_state == m_start
+	|| old_state == m_stop
+	|| old_state == m_bound_stream_socket
+	|| old_state == m_bound_unknown_socket
+	/* Assume it's OK to call "listen" more than once.  */
+	|| old_state == m_listening_stream_socket))
+    {
+      /* Complain about fncall on wrong type or in wrong phase.  */
+      tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
+      if (is_stream_socket_fd_p (old_state))
+	sm_ctxt->warn
+	  (node, stmt, fd_sval,
+	   make_unique<fd_phase_mismatch> (*this, diag_arg,
+					   cd.get_fndecl_for_call (),
+					   old_state,
+					   EXPECTED_PHASE_CAN_LISTEN));
+      else
+	sm_ctxt->warn
+	  (node, stmt, fd_sval,
+	   make_unique<fd_type_mismatch> (*this, diag_arg,
+					  cd.get_fndecl_for_call (),
+					  old_state,
+					  EXPECTED_TYPE_STREAM_SOCKET));
+      if (successful)
+	return false;
+    }
+
+  if (successful)
+    {
+      model->update_for_zero_return (cd, true);
+      sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval,
+			       m_listening_stream_socket);
+    }
+  else
+    {
+      /* Return -1; set errno.  */
+      model->update_for_int_cst_return (cd, -1, true);
+      model->set_errno (cd);
+      if (old_state == m_start)
+	sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval,
+				 m_bound_stream_socket);
+    }
+
+  return true;
+}
+
+/* Update the model and fd state for an outcome of a call to "accept",
+   where SUCCESSFUL indicate which of the two outcomes.
+   Return true if the outcome is feasible, or false to reject it.  */
+
+bool
+fd_state_machine::on_accept (const call_details &cd,
+			     bool successful,
+			     sm_context *sm_ctxt,
+			     const extrinsic_state &ext_state) const
+{
+  const gcall *stmt = cd.get_call_stmt ();
+  engine *eng = ext_state.get_engine ();
+  const supergraph *sg = eng->get_supergraph ();
+  const supernode *node = sg->get_supernode_for_stmt (stmt);
+  const svalue *fd_sval = cd.get_arg_svalue (0);
+  const svalue *address_sval = cd.get_arg_svalue (1);
+  const svalue *len_ptr_sval = cd.get_arg_svalue (2);
+  region_model *model = cd.get_model ();
+  state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
+
+  if (!address_sval->all_zeroes_p ())
+    {
+      region_model_manager *mgr = model->get_manager ();
+
+      /* We might have a union of various pointer types, rather than a
+	 pointer type; cast to (void *) before dereferencing.  */
+      address_sval = mgr->get_or_create_cast (ptr_type_node, address_sval);
+
+      const region *address_reg
+	= model->deref_rvalue (address_sval, cd.get_arg_tree (1),
+			       cd.get_ctxt ());
+      const region *len_reg
+	= model->deref_rvalue (len_ptr_sval, cd.get_arg_tree (2),
+			       cd.get_ctxt ());
+      const svalue *old_len_sval
+	= model->get_store_value (len_reg, cd.get_ctxt ());
+      tree len_ptr = cd.get_arg_tree (2);
+      tree star_len_ptr = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (len_ptr)),
+				  len_ptr,
+				  build_int_cst (TREE_TYPE (len_ptr), 0));
+      old_len_sval = model->check_for_poison (old_len_sval,
+					      star_len_ptr,
+					      cd.get_ctxt ());
+      if (successful)
+	{
+	  conjured_purge p (model, cd.get_ctxt ());
+	  const region *old_sized_address_reg
+	    = mgr->get_sized_region (address_reg,
+				     NULL_TREE,
+				     old_len_sval);
+	  const svalue *new_addr_sval
+	    = mgr->get_or_create_conjured_svalue (NULL_TREE,
+						  stmt,
+						  old_sized_address_reg,
+						  p);
+	  model->set_value (old_sized_address_reg, new_addr_sval,
+			    cd.get_ctxt ());
+	  const svalue *new_addr_len
+	    = mgr->get_or_create_conjured_svalue (NULL_TREE,
+						  stmt,
+						  len_reg,
+						  p);
+	  model->set_value (len_reg, new_addr_len, cd.get_ctxt ());
+	}
+    }
+
+  /* We expect a stream socket in the "listening" state.  */
+  if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state))
+    return false;
+
+  if (old_state == m_start)
+    /* If we were in the start state, assume we had the expected state.  */
+    sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval,
+			     m_listening_stream_socket);
+  else if (old_state == m_stop)
+    {
+      /* No further complaints.  */
+    }
+  else if (old_state != m_listening_stream_socket)
+    {
+      /* Complain about fncall on wrong type or in wrong phase.  */
+      tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
+      if (is_stream_socket_fd_p (old_state))
+	sm_ctxt->warn
+	  (node, stmt, fd_sval,
+	   make_unique<fd_phase_mismatch> (*this, diag_arg,
+					   cd.get_fndecl_for_call (),
+					   old_state,
+					   EXPECTED_PHASE_CAN_ACCEPT));
+      else
+	sm_ctxt->warn
+	  (node, stmt, fd_sval,
+	   make_unique<fd_type_mismatch> (*this, diag_arg,
+					  cd.get_fndecl_for_call (),
+					  old_state,
+					  EXPECTED_TYPE_STREAM_SOCKET));
+      if (successful)
+	return false;
+    }
+
+  if (successful)
+    {
+      /* Return new conjured FD in "connected" state.  */
+      if (gimple_call_lhs (stmt))
+	{
+	  conjured_purge p (model, cd.get_ctxt ());
+	  region_model_manager *mgr = model->get_manager ();
+	  const svalue *new_fd
+	    = mgr->get_or_create_conjured_svalue (integer_type_node,
+						  stmt,
+						  cd.get_lhs_region (),
+						  p);
+	  if (!add_constraint_ge_zero (model, new_fd, cd.get_ctxt ()))
+	    return false;
+	  sm_ctxt->on_transition (node, stmt, new_fd,
+				  m_start, m_connected_stream_socket);
+	  model->set_value (cd.get_lhs_region (), new_fd, cd.get_ctxt ());
+	}
+      else
+	sm_ctxt->warn (node, stmt, NULL_TREE,
+		       make_unique<fd_leak> (*this, NULL_TREE));
+    }
+  else
+    {
+      /* Return -1; set errno.  */
+      model->update_for_int_cst_return (cd, -1, true);
+      model->set_errno (cd);
+    }
+
+  return true;
+}
+
+/* Update the model and fd state for an outcome of a call to "connect",
+   where SUCCESSFUL indicate which of the two outcomes.
+   Return true if the outcome is feasible, or false to reject it.  */
+
+bool
+fd_state_machine::on_connect (const call_details &cd,
+			      bool successful,
+			      sm_context *sm_ctxt,
+			      const extrinsic_state &ext_state) const
+{
+  const gcall *stmt = cd.get_call_stmt ();
+  engine *eng = ext_state.get_engine ();
+  const supergraph *sg = eng->get_supergraph ();
+  const supernode *node = sg->get_supernode_for_stmt (stmt);
+  const svalue *fd_sval = cd.get_arg_svalue (0);
+  region_model *model = cd.get_model ();
+  state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
+
+  if (!check_for_new_socket_fd (cd, successful, sm_ctxt,
+				fd_sval, node, old_state,
+				EXPECTED_PHASE_CAN_CONNECT))
+    return false;
+
+  if (successful)
+    {
+      model->update_for_zero_return (cd, true);
+      state_t next_state = NULL;
+      if (old_state == m_new_stream_socket)
+	next_state = m_connected_stream_socket;
+      else if (old_state == m_new_datagram_socket)
+	/* It's legal to call connect on a datagram socket, potentially
+	   more than once.  We don't transition states for this.  */
+	next_state = m_new_datagram_socket;
+      else if (old_state == m_new_unknown_socket)
+	next_state = m_stop;
+      else if (old_state == m_start)
+	next_state = m_stop;
+      else if (old_state == m_stop)
+	next_state = m_stop;
+      else
+	gcc_unreachable ();
+      sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval, next_state);
+    }
+  else
+    {
+      /* Return -1; set errno.  */
+      model->update_for_int_cst_return (cd, -1, true);
+      model->set_errno (cd);
+      /* TODO: perhaps transition to a failed state, since the
+	 portable way to handle a failed "connect" is to close
+	 the socket and try again with a new socket.  */
+    }
+
+  return true;
+}
+
 void
 fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node,
 				const gimple *stmt, const svalue *lhs,
@@ -1215,7 +2191,9 @@  fd_state_machine::make_invalid_transitions_on_condition (
 bool
 fd_state_machine::can_purge_p (state_t s) const
 {
-  if (is_unchecked_fd_p (s) || is_valid_fd_p (s))
+  if (is_unchecked_fd_p (s)
+      || is_valid_fd_p (s)
+      || is_socket_fd_p (s))
     return false;
   else
     return true;
@@ -1234,30 +2212,130 @@  make_fd_state_machine (logger *logger)
   return new fd_state_machine (logger);
 }
 
+static bool
+get_fd_state (region_model_context *ctxt,
+	      sm_state_map **out_smap,
+	      const fd_state_machine **out_sm,
+	      unsigned *out_sm_idx,
+	      std::unique_ptr<sm_context> *out_sm_context)
+{
+  if (!ctxt)
+    return false;
+
+  const state_machine *sm;
+  if (!ctxt->get_fd_map (out_smap, &sm, out_sm_idx, out_sm_context))
+    return false;
+
+  gcc_assert (sm);
+
+  *out_sm = (const fd_state_machine *)sm;
+  return true;
+}
+
 /* Specialcase hook for handling pipe, for use by
    region_model::impl_call_pipe::success::update_model.  */
 
 void
 region_model::mark_as_valid_fd (const svalue *sval, region_model_context *ctxt)
 {
-  if (!ctxt)
+  sm_state_map *smap;
+  const fd_state_machine *fd_sm;
+  if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, NULL))
     return;
   const extrinsic_state *ext_state = ctxt->get_ext_state ();
   if (!ext_state)
     return;
+  fd_sm->mark_as_valid_fd (this, smap, sval, *ext_state);
+}
 
+/* Specialcase hook for handling "socket", for use by
+   region_model::impl_call_socket::outcome_of_socket::update_model.  */
+
+bool
+region_model::on_socket (const call_details &cd, bool successful)
+{
   sm_state_map *smap;
-  const state_machine *sm;
-  unsigned sm_idx;
-  if (!ctxt->get_fd_map (&smap, &sm, &sm_idx))
-    return;
+  const fd_state_machine *fd_sm;
+  std::unique_ptr<sm_context> sm_ctxt;
+  if (!get_fd_state (cd.get_ctxt (), &smap, &fd_sm, NULL, &sm_ctxt))
+    return true;
+  const extrinsic_state *ext_state = cd.get_ctxt ()->get_ext_state ();
+  if (!ext_state)
+    return true;
 
-  gcc_assert (smap);
-  gcc_assert (sm);
+  return fd_sm->on_socket (cd, successful, sm_ctxt.get (), *ext_state);
+}
+
+/* Specialcase hook for handling "bind", for use by
+   region_model::impl_call_bind::outcome_of_bind::update_model.  */
+
+bool
+region_model::on_bind (const call_details &cd, bool successful)
+{
+  sm_state_map *smap;
+  const fd_state_machine *fd_sm;
+  std::unique_ptr<sm_context> sm_ctxt;
+  if (!get_fd_state (cd.get_ctxt (), &smap, &fd_sm, NULL, &sm_ctxt))
+    return true;
+  const extrinsic_state *ext_state = cd.get_ctxt ()->get_ext_state ();
+  if (!ext_state)
+    return true;
+
+  return fd_sm->on_bind (cd, successful, sm_ctxt.get (), *ext_state);
+}
+
+/* Specialcase hook for handling "listen", for use by
+   region_model::impl_call_listen::outcome_of_listen::update_model.  */
+
+bool
+region_model::on_listen (const call_details &cd, bool successful)
+{
+  sm_state_map *smap;
+  const fd_state_machine *fd_sm;
+  std::unique_ptr<sm_context> sm_ctxt;
+  if (!get_fd_state (cd.get_ctxt (), &smap, &fd_sm, NULL, &sm_ctxt))
+    return true;
+  const extrinsic_state *ext_state = cd.get_ctxt ()->get_ext_state ();
+  if (!ext_state)
+    return true;
+
+  return fd_sm->on_listen (cd, successful, sm_ctxt.get (), *ext_state);
+}
+
+/* Specialcase hook for handling "accept", for use by
+   region_model::impl_call_accept::outcome_of_accept::update_model.  */
+
+bool
+region_model::on_accept (const call_details &cd, bool successful)
+{
+  sm_state_map *smap;
+  const fd_state_machine *fd_sm;
+  std::unique_ptr<sm_context> sm_ctxt;
+  if (!get_fd_state (cd.get_ctxt (), &smap, &fd_sm, NULL, &sm_ctxt))
+    return true;
+  const extrinsic_state *ext_state = cd.get_ctxt ()->get_ext_state ();
+  if (!ext_state)
+    return true;
+
+  return fd_sm->on_accept (cd, successful, sm_ctxt.get (), *ext_state);
+}
 
-  const fd_state_machine &fd_sm = (const fd_state_machine &)*sm;
+/* Specialcase hook for handling "connect", for use by
+   region_model::impl_call_connect::outcome_of_connect::update_model.  */
+
+bool
+region_model::on_connect (const call_details &cd, bool successful)
+{
+  sm_state_map *smap;
+  const fd_state_machine *fd_sm;
+  std::unique_ptr<sm_context> sm_ctxt;
+  if (!get_fd_state (cd.get_ctxt (), &smap, &fd_sm, NULL, &sm_ctxt))
+    return true;
+  const extrinsic_state *ext_state = cd.get_ctxt ()->get_ext_state ();
+  if (!ext_state)
+    return true;
 
-  fd_sm.mark_as_valid_fd (this, smap, sval, *ext_state);
+  return fd_sm->on_connect (cd, successful, sm_ctxt.get (), *ext_state);
 }
 
 } // namespace ana
diff --git a/gcc/analyzer/sm-fd.dot b/gcc/analyzer/sm-fd.dot
index 175daae44ae..0b2b31996ec 100644
--- a/gcc/analyzer/sm-fd.dot
+++ b/gcc/analyzer/sm-fd.dot
@@ -46,6 +46,29 @@  digraph "fd" {
   /* State for a file descriptor that has been closed.  */
   closed;
 
+  /* States for FDs relating to socket APIs.  */
+
+  /* Result of successful "socket" with SOCK_DGRAM.  */
+  new_datagram_socket;
+  /* Result of successful "socket" with SOCK_STREAM.  */
+  new_stream_socket;
+  /* Result of successful "socket" with unknown type.  */
+  new_unknown_socket;
+
+  /* The above after a successful call to "bind".  */
+  bound_datagram_socket;
+  bound_stream_socket;
+  bound_unknown_socket;
+
+  /* A bound socket after a successful call to "listen" (stream or unknown).  */
+  listening_stream_socket;
+
+  /* (i) the new FD as a result of a succesful call to "accept" on a
+      listening socket (via a passive open), or
+     (ii) an active socket after a successful call to "connect"
+     (via an active open).  */
+  connected_stream_socket;
+
   /* State for a file descriptor that we do not want to track anymore . */
   stop;
 
@@ -68,6 +91,14 @@  digraph "fd" {
   valid_read_only -> closed [label="on 'close(X);'"];
   valid_write_only -> closed [label="on 'close(X);'"];
   constant_fd -> closed [label="on 'close(X);'"];
+  new_datagram_socket -> closed [label="on 'close(X);'"];
+  new_stream_socket -> closed [label="on 'close(X);'"];
+  new_unknown_socket -> closed [label="on 'close(X);'"];
+  bound_datagram_socket -> closed [label="on 'close(X);'"];
+  bound_stream_socket -> closed [label="on 'close(X);'"];
+  bound_unknown_socket -> closed [label="on 'close(X);'"];
+  listening_stream_socket -> closed [label="on 'close(X);'"];
+  connected_stream_socket -> closed [label="on 'close(X);'"];
   closed -> stop [label="on 'close(X);':\nWarn('double close')"];
 
   /* On "read".  */
@@ -91,6 +122,31 @@  digraph "fd" {
   /* On "pipe".  */
   start -> valid_read_write [label="when 'pipe()' succeeds"];
 
+  /* On "socket".  */
+  start -> new_datagram_socket [label="when 'socket(..., SOCK_DGRAM, ...)' succeeds"];
+  start -> new_stream_socket [label="when 'socket(..., SOCK_STREAM, ...)' succeeds"];
+  start -> new_unknown_socket [label="when 'socket(..., ..., ...)' succeeds"];
+
+  /* On "bind".  */
+  start -> bound_unknown_socket [label="when 'bind(X, ...)' succeeds"];
+  new_stream_socket -> bound_stream_socket [label="when 'bind(X, ...)' succeeds"];
+  new_datagram_socket -> bound_datagram_socket [label="when 'bind(X, ...)' succeeds"];
+  new_unknown_socket -> bound_unknown_socket [label="when 'bind(X, ...)' succeeds"];
+
+  /* On "listen".  */
+  start -> listening_stream_socket [label="when 'listen(X, ...)' succeeds"];
+  bound_stream_socket -> listening_stream_socket [label="when 'listen(X, ...)' succeeds"];
+  bound_unknown_socket -> listening_stream_socket [label="when 'listen(X, ...)' succeeds"];
+
+  /* On "accept".  */
+  start -> connected_stream_socket [label="when 'accept(OTHER, ...)' succeeds on a listening_stream_socket"];
+
+  /* On "connect".  */
+  new_stream_socket -> connected_stream_socket [label="when 'connect(X, ...)' succeeds"];
+  new_datagram_socket -> new_datagram_socket [label="when 'connect(X, ...)' succeeds"];
+  new_unknown_socket -> stop [label="when 'connect(X, ...)' succeeds"];
+  start -> stop [label="when 'connect(X, ...)' succeeds"];
+
   /* on_condition.  */
   unchecked_read_write -> valid_read_write [label="on 'X >= 0'"];
   unchecked_read_only -> valid_read_only [label="on 'X >= 0'"];
@@ -106,4 +162,12 @@  digraph "fd" {
   valid_read_write -> stop [label="on leak:\nWarn('leak')"];
   valid_read_only -> stop [label="on leak:\nWarn('leak')"];
   valid_write_only -> stop [label="on leak:\nWarn('leak')"];
+  new_datagram_socket -> stop [label="on leak:\nWarn('leak')"];
+  new_stream_socket -> stop [label="on leak:\nWarn('leak')"];
+  new_unknown_socket -> stop [label="on leak:\nWarn('leak')"];
+  bound_datagram_socket -> stop [label="on leak:\nWarn('leak')"];
+  bound_stream_socket -> stop [label="on leak:\nWarn('leak')"];
+  bound_unknown_socket -> stop [label="on leak:\nWarn('leak')"];
+  listening_stream_socket -> stop [label="on leak:\nWarn('leak')"];
+  connected_stream_socket -> stop [label="on leak:\nWarn('leak')"];
 }
diff --git a/gcc/doc/gcc/gcc-command-options/option-summary.rst b/gcc/doc/gcc/gcc-command-options/option-summary.rst
index d068f98feac..f99ce2346c0 100644
--- a/gcc/doc/gcc/gcc-command-options/option-summary.rst
+++ b/gcc/doc/gcc/gcc-command-options/option-summary.rst
@@ -291,6 +291,8 @@  in the following sections.
   :option:`-Wno-analyzer-fd-access-mode-mismatch` |gol|
   :option:`-Wno-analyzer-fd-double-close` |gol|
   :option:`-Wno-analyzer-fd-leak` |gol|
+  :option:`-Wno-analyzer-fd-phase-mismatch` |gol|
+  :option:`-Wno-analyzer-fd-type-mismatch` |gol|
   :option:`-Wno-analyzer-fd-use-after-close` |gol|
   :option:`-Wno-analyzer-fd-use-without-check` |gol|
   :option:`-Wno-analyzer-file-leak` |gol|
diff --git a/gcc/doc/gcc/gcc-command-options/options-that-control-static-analysis.rst b/gcc/doc/gcc/gcc-command-options/options-that-control-static-analysis.rst
index 32a626c16a9..9b081f93fce 100644
--- a/gcc/doc/gcc/gcc-command-options/options-that-control-static-analysis.rst
+++ b/gcc/doc/gcc/gcc-command-options/options-that-control-static-analysis.rst
@@ -27,6 +27,8 @@  Options That Control Static Analysis
   :option:`-Wanalyzer-fd-access-mode-mismatch` |gol|
   :option:`-Wanalyzer-fd-double-close` |gol|
   :option:`-Wanalyzer-fd-leak` |gol|
+  :option:`-Wanalyzer-fd-phase-mismatch` |gol|
+  :option:`-Wanalyzer-fd-type-mismatch` |gol|
   :option:`-Wanalyzer-fd-use-after-close` |gol|
   :option:`-Wanalyzer-fd-use-without-check` |gol|
   :option:`-Wanalyzer-file-leak` |gol|
@@ -228,6 +230,39 @@  Options That Control Static Analysis
 
   Default setting; overrides :option:`-Wno-analyzer-fd-leak`.
 
+.. option:: -Wno-analyzer-fd-phase-mismatch
+
+  This warning requires :option:`-fanalyzer`, which enables it; use
+  :option:`-Wno-analyzer-fd-phase-mismatch`
+  to disable it.
+
+  This diagnostic warns for paths through code in which an operation is
+  attempted in the wrong phase of a file descriptor's lifetime.
+  For example, it will warn on attempts to call ``accept`` on a stream
+  socket that has not yet had ``listen`` successfully called on it.
+
+  See `CWE-666: Operation on Resource in Wrong Phase of Lifetime <https://cwe.mitre.org/data/definitions/666.html>`_.
+
+.. option:: -Wanalyzer-fd-phase-mismatch
+
+  Default setting; overrides :option:`-Wno-analyzer-fd-phase-mismatch`.
+
+.. option:: -Wno-analyzer-fd-type-mismatch
+
+  This warning requires :option:`-fanalyzer`, which enables it; use
+  :option:`-Wno-analyzer-fd-type-mismatch`
+  to disable it.
+
+  This diagnostic warns for paths through code in which an
+  operation is attempted on the wrong type of file descriptor.
+  For example, it will warn on attempts to use socket operations
+  on a file descriptor obtained via ``open``, or when attempting
+  to use a stream socket operation on a datagram socket.
+
+.. option:: -Wanalyzer-fd-type-mismatch
+
+  Default setting; overrides :option:`-Wno-analyzer-fd-type-mismatch`.
+
 .. option:: -Wno-analyzer-fd-use-after-close
 
   This warning requires :option:`-fanalyzer`, which enables it; use
@@ -798,6 +833,7 @@  of the following functions for working with file descriptors:
 * ``pipe`` and ``pipe2``
 * ``read``
 * ``write``
+* ``socket``, ``bind``, ``listen``, ``accept`` and ``connect``
 
 of the following functions for working with ``<stdio.h>`` streams:
 
@@ -888,6 +924,8 @@  The following options control the analyzer.
     :option:`-Wanalyzer-fd-access-mode-mismatch`  |gol|
     :option:`-Wanalyzer-fd-double-close`  |gol|
     :option:`-Wanalyzer-fd-leak`  |gol|
+    :option:`-Wanalyzer-fd-phase-mismatch` |gol|
+    :option:`-Wanalyzer-fd-type-mismatch` |gol|
     :option:`-Wanalyzer-fd-use-after-close`  |gol|
     :option:`-Wanalyzer-fd-use-without-check`  |gol|
     :option:`-Wanalyzer-file-leak`  |gol|
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-accept.c b/gcc/testsuite/gcc.dg/analyzer/fd-accept.c
new file mode 100644
index 00000000000..36cc7af7184
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-accept.c
@@ -0,0 +1,69 @@ 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+int test_accept (int fd, struct sockaddr *addr, socklen_t *addrlen)
+{
+  return accept (fd, addr, addrlen);
+}
+
+void test_accept_leak_no_lhs (int fd, struct sockaddr *addr, socklen_t *addrlen)
+{
+  accept (fd, addr, addrlen); /* { dg-warning "leak of file descriptor" } */
+}
+
+void test_accept_leak_with_lhs (int fd, struct sockaddr *addr, socklen_t *addrlen)
+{
+  int newfd = accept (fd, addr, addrlen); /* { dg-message "socket created here" } */
+} /* { dg-warning "leak of file descriptor 'newfd'" } */
+
+int test_accept_null_addr (int fd)
+{
+  return accept (fd, NULL, 0);
+}
+
+int test_accept_uninit_addrlen (int fd)
+{
+  struct sockaddr_storage addr;
+  socklen_t addr_len;
+  return accept (fd, (struct sockaddr *)&addr, &addr_len); /* { dg-warning "use of uninitialized value 'addr_len'" } */
+}
+
+int test_accept_writes_to_addr_and_len (int fd)
+{
+  struct sockaddr_storage addr;
+  socklen_t addr_len = sizeof (addr);
+  __analyzer_eval (addr_len == sizeof (addr)); /* { dg-warning "TRUE" } */
+  int newfd = accept (fd, (struct sockaddr *)&addr, &addr_len);
+  if (newfd == -1)
+    return newfd;
+  /* Check that the analyzer considers addr and addr_len to
+     have been written to.  */
+  __analyzer_eval (((char *)&addr)[0]); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (addr_len == sizeof (addr)); /* { dg-warning "UNKNOWN" } */
+  return newfd;
+}
+
+void test_accept_on_new_datagram_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0);
+  if (fd == -1)
+    return;
+  accept (fd, NULL, NULL); /* { dg-message "'accept' on datagram socket file descriptor 'fd' \\\[-Wanalyzer-fd-type-mismatch\\\]" "warning" } */
+  /* { dg-message "'accept' expects a stream socket file descriptor but 'fd' is a datagram socket" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+int test_accept_on_accept (int fd_a)
+{
+  int fd_b = accept (fd_a, NULL, 0);
+  if (fd_b == -1)
+    return -1;
+
+  int fd_c = accept (fd_b, NULL, 0);  /* { dg-warning "'accept' on file descriptor 'fd_b' in wrong phase \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */
+  /* { dg-message "'accept' expects a listening stream socket file descriptor but 'fd_b' is connected" "final event" { target *-*-* } .-1 } */
+
+  return fd_b;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-bind.c b/gcc/testsuite/gcc.dg/analyzer/fd-bind.c
new file mode 100644
index 00000000000..6f91bc4b794
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-bind.c
@@ -0,0 +1,74 @@ 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+void test_bind (int fd, const char *sockname)
+{
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  if (bind (fd, (struct sockaddr *)&addr, sizeof (addr)) == -1)
+    __analyzer_dump_path (); /* { dg-message "path" } */
+  else
+    __analyzer_dump_path (); /* { dg-message "path" } */
+}
+
+void test_null_bind (int fd)
+{
+  errno = 0;
+  int result = bind (fd, NULL, 0);
+  __analyzer_eval (result == -1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+}
+
+void test_double_bind (int fd, const char *sockname)
+{
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr));
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on file descriptor 'fd' in wrong phase \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */
+  /* { dg-message "'bind' expects a new socket file descriptor but 'fd' has already been bound" "final event" { target *-*-* } .-1 } */
+}
+
+int test_uninit_addr (int fd, const char *sockname)
+{
+  struct sockaddr_un addr;
+  return bind (fd, (struct sockaddr *)&addr, sizeof (addr));
+  // TODO: complain about uninit addr.
+}
+
+void test_bind_after_connect (int fd, const char *sockname,
+				const struct sockaddr *caddr, socklen_t caddrlen)
+{
+  if (connect (fd, caddr, caddrlen) == -1)
+    return;
+
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr));
+  /* TODO: we don't warn for this; after the plain "connect" we're
+     in the stop state.  */
+}
+
+void test_bind_after_accept (int fd, const char *sockname)
+{
+  int afd = accept (fd, NULL, NULL);
+  if (afd == -1)
+    return;
+
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (afd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on file descriptor 'afd' in wrong phase \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */
+  /* { dg-message "'bind' expects a new socket file descriptor but 'afd' is already connected" "final event" { target *-*-* } .-1 } */
+
+  close (afd);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-connect.c b/gcc/testsuite/gcc.dg/analyzer/fd-connect.c
new file mode 100644
index 00000000000..1ab54d01f36
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-connect.c
@@ -0,0 +1,46 @@ 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+int test_connect (int sockfd, const struct sockaddr *addr,
+		  socklen_t addrlen)
+{
+  return connect (sockfd, addr, addrlen);
+}
+
+void test_null_connect (int fd)
+{
+  errno = 0;
+  int result = connect (fd, NULL, 0);
+  __analyzer_eval (result == -1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+}
+
+int test_uninit_addr (int fd, const char *sockname)
+{
+  struct sockaddr_un addr;
+  return connect (fd, (struct sockaddr *)&addr, sizeof (addr));
+  // TODO: complain about uninit addr.
+}
+
+void test_connect_after_bind (const char *sockname,
+			      const struct sockaddr *baddr, socklen_t baddrlen,
+			      const struct sockaddr *caddr, socklen_t caddrlen)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */
+  if (fd == -1)
+    return;
+
+  if (bind (fd, baddr, baddrlen) == -1)
+    {
+      close (fd);
+      return;
+    }
+
+  connect (fd, caddr, caddrlen); /* { dg-warning "'connect' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'connect' expects a new socket file descriptor but 'fd' is bound" "final event" { target *-*-* } .-1 } */
+
+  close (fd);      
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-datagram-socket.c b/gcc/testsuite/gcc.dg/analyzer/fd-datagram-socket.c
new file mode 100644
index 00000000000..045bdfa32d3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-datagram-socket.c
@@ -0,0 +1,108 @@ 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+void test_leak_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0); /* { dg-message "datagram socket created here" } */
+} /* { dg-warning "leak of file descriptor 'fd'" } */
+
+void test_leak_socket_no_lhs (void)
+{
+  socket (AF_UNIX, SOCK_DGRAM, 0);  /* { dg-warning "leak of file descriptor" } */
+}
+
+void test_close_unchecked_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0);
+  close (fd);
+}
+
+void test_close_checked_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0);
+  if (fd == -1)
+    return;
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-datagram-socket'" } */
+  close (fd);
+}
+
+void test_leak_checked_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0); /* { dg-message "datagram socket created here" } */
+  if (fd == -1) /* { dg-warning "leak of file descriptor 'fd'" } */
+    return;
+  // TODO: strange location for leak message
+}
+
+void test_bind (const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0);
+  if (fd == -1)
+    return;
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-datagram-socket'" } */
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr));
+  close (fd);
+}
+
+void test_bind_on_unchecked_socket (const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0); /* { dg-message "when 'socket' fails" } */
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on possibly invalid file descriptor 'fd'" } */
+  close (fd);
+}
+
+void test_leak_of_bound_socket (const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0); /* { dg-message "datagram socket created here" } */
+  if (fd == -1)
+    return;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "leak of file descriptor 'fd'" } */
+}
+
+void test_listen_on_datagram_socket_without_bind (void)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0); /* { dg-message "datagram socket created here" }  */
+  if (fd == -1)
+    return;
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-datagram-socket'" } */
+  listen (fd, 5); /* { dg-warning "'listen' on datagram socket file descriptor 'fd' \\\[-Wanalyzer-fd-type-mismatch\\\]" "warning" } */
+  /* { dg-message "'listen' expects a stream socket file descriptor but 'fd' is a datagram socket" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+void test_listen_on_datagram_socket_with_bind (const char *sockname)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0); /* { dg-message "datagram socket created here" }  */
+  if (fd == -1)
+    return;
+
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-datagram-socket'" } */
+
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  if (bind (fd, (struct sockaddr *)&addr, sizeof (addr)) == -1) /* { dg message "datagram socket bound here" } */
+    {
+      close (fd);
+      return;
+    }
+  listen (fd, 5); /* { dg-warning "'listen' on datagram socket file descriptor 'fd' \\\[-Wanalyzer-fd-type-mismatch\\\]" "warning" } */
+  /* { dg-message "'listen' expects a stream socket file descriptor but 'fd' is a datagram socket" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-connection-server.c b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-connection-server.c
new file mode 100644
index 00000000000..1ff902894af
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-connection-server.c
@@ -0,0 +1,133 @@ 
+/* Example from glibc manual (16.9.7).  */
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define PORT    5555
+#define MAXMSG  512
+
+int
+make_socket (uint16_t port)
+{
+  int sock;
+  struct sockaddr_in name;
+
+  /* Create the socket. */
+  sock = socket (PF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      perror ("socket");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Give the socket a name. */
+  name.sin_family = AF_INET;
+  name.sin_port = htons (port);
+  name.sin_addr.s_addr = htonl (INADDR_ANY);
+  if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
+    {
+      perror ("bind");
+      exit (EXIT_FAILURE);
+    }
+
+  return sock;
+}
+
+int
+read_from_client (int filedes)
+{
+  char buffer[MAXMSG];
+  int nbytes;
+
+  nbytes = read (filedes, buffer, MAXMSG);
+  if (nbytes < 0)
+    {
+      /* Read error. */
+      perror ("read");
+      exit (EXIT_FAILURE);
+    }
+  else if (nbytes == 0)
+    /* End-of-file. */
+    return -1;
+  else
+    {
+      /* Data read. */
+      fprintf (stderr, "Server: got message: `%s'\n", buffer);
+      return 0;
+    }
+}
+
+int
+main (void)
+{
+  int sock;
+  fd_set active_fd_set, read_fd_set;
+  int i;
+  struct sockaddr_in clientname;
+  socklen_t size;
+
+  /* Create the socket and set it up to accept connections. */
+  sock = make_socket (PORT);
+  if (listen (sock, 1) < 0)
+    {
+      perror ("listen");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Initialize the set of active sockets. */
+  FD_ZERO (&active_fd_set);
+  FD_SET (sock, &active_fd_set);
+
+  while (1)
+    {
+      /* Block until input arrives on one or more active sockets. */
+      read_fd_set = active_fd_set;
+      if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
+        {
+          perror ("select");
+          exit (EXIT_FAILURE);
+        }
+
+      /* Service all the sockets with input pending. */
+      for (i = 0; i < FD_SETSIZE; ++i)
+        if (FD_ISSET (i, &read_fd_set))
+          {
+            if (i == sock)
+              {
+                /* Connection request on original socket. */
+                int new;
+                size = sizeof (clientname);
+                new = accept (sock,
+                              (struct sockaddr *) &clientname,
+                              &size);
+                if (new < 0)
+                  {
+                    perror ("accept");
+                    exit (EXIT_FAILURE);
+                  }
+                fprintf (stderr,
+                         "Server: connect from host %s, port %hd.\n",
+                         inet_ntoa (clientname.sin_addr),
+                         ntohs (clientname.sin_port));
+                FD_SET (new, &active_fd_set);
+              }
+            else
+              {
+                /* Data arriving on an already-connected socket. */
+                if (read_from_client (i) < 0)
+                  {
+                    close (i);
+                    FD_CLR (i, &active_fd_set);
+                  }
+              }
+          }
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-socket.c b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-socket.c
new file mode 100644
index 00000000000..f96da8101cc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-socket.c
@@ -0,0 +1,62 @@ 
+/* Example from glibc manual (16.9.6).  */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#define PORT            5555
+#define MESSAGE         "Yow!!! Are we having fun yet?!?"
+#define SERVERHOST      "www.gnu.org"
+
+void
+write_to_server (int filedes)
+{
+  int nbytes;
+
+  nbytes = write (filedes, MESSAGE, strlen (MESSAGE) + 1);
+  if (nbytes < 0)
+    {
+      perror ("write");
+      exit (EXIT_FAILURE);
+    }
+}
+
+
+int
+main (void)
+{
+  extern void init_sockaddr (struct sockaddr_in *name,
+                             const char *hostname,
+                             uint16_t port);
+  int sock;
+  struct sockaddr_in servername;
+
+  /* Create the socket. */
+  sock = socket (PF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      perror ("socket (client)");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Connect to the server. */
+  init_sockaddr (&servername, SERVERHOST, PORT);
+  if (0 > connect (sock,
+                   (struct sockaddr *) &servername,
+                   sizeof (servername)))
+    {
+      perror ("connect (client)");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Send data to the server. */
+  write_to_server (sock);
+  close (sock);
+  exit (EXIT_SUCCESS);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-client.c b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-client.c
new file mode 100644
index 00000000000..888c751e88d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-client.c
@@ -0,0 +1,56 @@ 
+/* Example from the glibc manual (16.10.4).  */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "fd-glibc-make_named_socket.h"
+
+#define SERVER  "/tmp/serversocket"
+#define CLIENT  "/tmp/mysocket"
+#define MAXMSG  512
+#define MESSAGE "Yow!!! Are we having fun yet?!?"
+
+int
+main (void)
+{
+  int sock;
+  char message[MAXMSG];
+  struct sockaddr_un name;
+  size_t size;
+  int nbytes;
+
+  /* Make the socket. */
+  sock = make_named_socket (CLIENT);
+
+  /* Initialize the server socket address. */
+  name.sun_family = AF_LOCAL;
+  strcpy (name.sun_path, SERVER);
+  size = strlen (name.sun_path) + sizeof (name.sun_family);
+
+  /* Send the datagram. */
+  nbytes = sendto (sock, MESSAGE, strlen (MESSAGE) + 1, 0,
+                   (struct sockaddr *) & name, size);
+  if (nbytes < 0)
+    {
+      perror ("sendto (client)");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Wait for a reply. */
+  nbytes = recvfrom (sock, message, MAXMSG, 0, NULL, 0);
+  if (nbytes < 0)
+    {
+      perror ("recfrom (client)");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Print a diagnostic message. */
+  fprintf (stderr, "Client: got message: %s\n", message);
+
+  /* Clean up. */
+  remove (CLIENT);
+  close (sock);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-socket.c b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-socket.c
new file mode 100644
index 00000000000..b8b68768c34
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-socket.c
@@ -0,0 +1,52 @@ 
+/* Example from glibc manual (16.10.3).  */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include "fd-glibc-make_named_socket.h"
+
+#define SERVER  "/tmp/serversocket"
+#define MAXMSG  512
+
+int
+main (void)
+{
+  int sock;
+  char message[MAXMSG];
+  struct sockaddr_un name;
+  socklen_t size;
+  int nbytes;
+
+  /* Remove the filename first, it’s ok if the call fails */
+  unlink (SERVER);
+
+  /* Make the socket, then loop endlessly. */
+  sock = make_named_socket (SERVER);
+  while (1)
+    {
+      /* Wait for a datagram. */
+      size = sizeof (name);
+      nbytes = recvfrom (sock, message, MAXMSG, 0,
+                         (struct sockaddr *) & name, &size);
+      if (nbytes < 0)
+        {
+          perror ("recfrom (server)");
+          exit (EXIT_FAILURE);
+        }
+
+      /* Give a diagnostic message. */
+      fprintf (stderr, "Server: got message: %s\n", message);
+
+      /* Bounce the message back to the sender. */
+      nbytes = sendto (sock, message, nbytes, 0,
+                       (struct sockaddr *) & name, size);
+      if (nbytes < 0)
+        {
+          perror ("sendto (server)");
+          exit (EXIT_FAILURE);
+        }
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-glibc-make_named_socket.h b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-make_named_socket.h
new file mode 100644
index 00000000000..bdb6de0ae15
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-glibc-make_named_socket.h
@@ -0,0 +1,47 @@ 
+/* Example of Local-Namespace Sockets from the glibc manual (16.5.3).  */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+int
+make_named_socket (const char *filename)
+{
+  struct sockaddr_un name;
+  int sock;
+  size_t size;
+
+  /* Create the socket. */
+  sock = socket (PF_LOCAL, SOCK_DGRAM, 0);
+  if (sock < 0)
+    {
+      perror ("socket");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Bind a name to the socket. */
+  name.sun_family = AF_LOCAL;
+  strncpy (name.sun_path, filename, sizeof (name.sun_path));
+  name.sun_path[sizeof (name.sun_path) - 1] = '\0';
+
+  /* The size of the address is
+     the offset of the start of the filename,
+     plus its length (not including the terminating null byte).
+     Alternatively you can just do:
+     size = SUN_LEN (&name);
+ */
+  size = (offsetof (struct sockaddr_un, sun_path)
+          + strlen (name.sun_path));
+
+  if (bind (sock, (struct sockaddr *) &name, size) < 0)
+    {
+      perror ("bind");
+      exit (EXIT_FAILURE);
+    }
+
+  return sock;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-listen.c b/gcc/testsuite/gcc.dg/analyzer/fd-listen.c
new file mode 100644
index 00000000000..1f54a8f2953
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-listen.c
@@ -0,0 +1,63 @@ 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+int test_listen (int fd, int backlog)
+{
+  return listen (fd, backlog);
+}
+
+/* Some systems seem to allow repeated calls to listen.  */
+
+void test_double_listen (int fd, int backlog)
+{
+  listen (fd, backlog);
+  listen (fd, backlog);
+}
+
+void test_listen_before_bind (int fd, const char *sockname)
+{
+  if (listen (fd, 5) == -1) /* { dg-message "stream socket marked as passive here via 'listen'" } */
+    return;
+
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'bind' expects a new socket file descriptor but 'fd' is already listening" "final event" { target *-*-* } .-1 } */
+}
+
+void test_listen_on_unchecked_bind (int fd, const char *sockname)
+{
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-message "when 'bind' fails" } */
+  listen (fd, 5); /* { dg-warning "'listen' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'listen' expects a bound stream socket file descriptor but 'fd' has not yet been bound" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+void test_listen_on_new_datagram_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0);
+  if (fd == -1)
+    return;
+  listen (fd, 5); /* { dg-message "'listen' on datagram socket file descriptor 'fd' \\\[-Wanalyzer-fd-type-mismatch\\\]" "warning" } */
+  /* { dg-message "'listen' expects a stream socket file descriptor but 'fd' is a datagram socket" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+void test_listed_on_connected_socket (int fd)
+{
+  int afd = accept (fd, NULL, 0);
+  if (afd == -1)
+    return;
+  listen (afd, 5); /* { dg-warning "'listen' on file descriptor 'afd' in wrong phase" "warning" } */
+  /* { dg-message "'listen' expects a bound stream socket file descriptor but 'afd' is connected" "final event" { target *-*-* } .-1 } */
+  close (afd);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-manpage-getaddrinfo-client.c b/gcc/testsuite/gcc.dg/analyzer/fd-manpage-getaddrinfo-client.c
new file mode 100644
index 00000000000..d9c3ff05de8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-manpage-getaddrinfo-client.c
@@ -0,0 +1,122 @@ 
+/* Example from getaddrinfo.3 manpage, which has this license:
+
+Copyright (c) 2007, 2008 Michael Kerrisk <mtk.manpages@gmail.com>
+and Copyright (c) 2006 Ulrich Drepper <drepper@redhat.com>
+A few pieces of an earlier version remain:
+Copyright 2000, Sam Varshavchik <mrsam@courier-mta.com>
+
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Since the Linux kernel and libraries are constantly changing, this
+manual page may be incorrect or out-of-date.  The author(s) assume no
+responsibility for errors or omissions, or for damages resulting from
+the use of the information contained herein.  The author(s) may not
+have taken the same level of care in the production of this manual,
+which is licensed free of charge, as they might when working
+professionally.
+
+Formatted or processed versions of this manual, if unaccompanied by
+the source, must acknowledge the copyright and authors of this work.
+*/
+
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define BUF_SIZE 500
+
+int
+main(int argc, char *argv[])
+{
+  struct addrinfo hints;
+  struct addrinfo *result, *rp;
+  int sfd, s;
+  size_t len;
+  ssize_t nread;
+  char buf[BUF_SIZE];
+
+  if (argc < 3) {
+    fprintf(stderr, "Usage: %s host port msg...\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+
+  /* Obtain address(es) matching host/port. */
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
+  hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
+  hints.ai_flags = 0;
+  hints.ai_protocol = 0;          /* Any protocol */
+
+  s = getaddrinfo(argv[1], argv[2], &hints, &result);
+  if (s != 0) {
+    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
+    exit(EXIT_FAILURE);
+  }
+
+  /* getaddrinfo() returns a list of address structures.
+     Try each address until we successfully connect(2).
+     If socket(2) (or connect(2)) fails, we (close the socket
+     and) try the next address. */
+
+  for (rp = result; rp != NULL; rp = rp->ai_next) {
+    sfd = socket(rp->ai_family, rp->ai_socktype,
+		 rp->ai_protocol);
+    if (sfd == -1)
+      continue;
+
+    if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
+      break;                  /* Success */
+
+    close(sfd);
+  }
+
+  freeaddrinfo(result);           /* No longer needed */
+
+  if (rp == NULL) {               /* No address succeeded */
+    fprintf(stderr, "Could not connect\n");
+    exit(EXIT_FAILURE);
+  }
+
+  /* Send remaining command-line arguments as separate
+     datagrams, and read responses from server. */
+
+  for (int j = 3; j < argc; j++) {
+    len = strlen(argv[j]) + 1;
+    /* +1 for terminating null byte */
+
+    if (len > BUF_SIZE) {
+      fprintf(stderr,
+	      "Ignoring long message in argument %d\n", j);
+      continue;
+    }
+
+    if (write(sfd, argv[j], len) != len) {
+      fprintf(stderr, "partial/failed write\n");
+      exit(EXIT_FAILURE);
+    }
+
+    nread = read(sfd, buf, BUF_SIZE);
+    if (nread == -1) {
+      perror("read");
+      exit(EXIT_FAILURE);
+    }
+
+    printf("Received %zd bytes: %s\n", nread, buf);
+  }
+
+  exit(EXIT_SUCCESS);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-mappage-getaddrinfo-server.c b/gcc/testsuite/gcc.dg/analyzer/fd-mappage-getaddrinfo-server.c
new file mode 100644
index 00000000000..66398e834cc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-mappage-getaddrinfo-server.c
@@ -0,0 +1,119 @@ 
+/* Example from getaddrinfo.3 manpage, which has this license:
+
+Copyright (c) 2007, 2008 Michael Kerrisk <mtk.manpages@gmail.com>
+and Copyright (c) 2006 Ulrich Drepper <drepper@redhat.com>
+A few pieces of an earlier version remain:
+Copyright 2000, Sam Varshavchik <mrsam@courier-mta.com>
+
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Since the Linux kernel and libraries are constantly changing, this
+manual page may be incorrect or out-of-date.  The author(s) assume no
+responsibility for errors or omissions, or for damages resulting from
+the use of the information contained herein.  The author(s) may not
+have taken the same level of care in the production of this manual,
+which is licensed free of charge, as they might when working
+professionally.
+
+Formatted or processed versions of this manual, if unaccompanied by
+the source, must acknowledge the copyright and authors of this work.
+*/
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#define BUF_SIZE 500
+
+int
+main(int argc, char *argv[])
+{
+  struct addrinfo hints;
+  struct addrinfo *result, *rp;
+  int sfd, s;
+  struct sockaddr_storage peer_addr;
+  socklen_t peer_addr_len;
+  ssize_t nread;
+  char buf[BUF_SIZE];
+
+  if (argc != 2) {
+    fprintf(stderr, "Usage: %s port\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
+  hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
+  hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
+  hints.ai_protocol = 0;          /* Any protocol */
+  hints.ai_canonname = NULL;
+  hints.ai_addr = NULL;
+  hints.ai_next = NULL;
+
+  s = getaddrinfo(NULL, argv[1], &hints, &result);
+  if (s != 0) {
+    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
+    exit(EXIT_FAILURE);
+  }
+
+  /* getaddrinfo() returns a list of address structures.
+     Try each address until we successfully bind(2).
+     If socket(2) (or bind(2)) fails, we (close the socket
+     and) try the next address. */
+
+  for (rp = result; rp != NULL; rp = rp->ai_next) {
+    sfd = socket(rp->ai_family, rp->ai_socktype,
+		 rp->ai_protocol);
+    if (sfd == -1)
+      continue;
+
+    if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
+      break;                  /* Success */
+
+    close(sfd);
+  }
+
+  freeaddrinfo(result);           /* No longer needed */
+
+  if (rp == NULL) {               /* No address succeeded */
+    fprintf(stderr, "Could not bind\n");
+    exit(EXIT_FAILURE);
+  }
+
+  /* Read datagrams and echo them back to sender. */
+
+  for (;;) {
+    peer_addr_len = sizeof(peer_addr);
+    nread = recvfrom(sfd, buf, BUF_SIZE, 0,
+		     (struct sockaddr *) &peer_addr, &peer_addr_len);
+    if (nread == -1)
+      continue;               /* Ignore failed request */
+
+    char host[NI_MAXHOST], service[NI_MAXSERV];
+
+    s = getnameinfo((struct sockaddr *) &peer_addr,
+		    peer_addr_len, host, NI_MAXHOST,
+		    service, NI_MAXSERV, NI_NUMERICSERV);
+    if (s == 0)
+      printf("Received %zd bytes from %s:%s\n",
+	     nread, host, service);
+    else
+      fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));
+    
+    if (sendto(sfd, buf, nread, 0,
+	       (struct sockaddr *) &peer_addr,
+	       peer_addr_len) != nread)
+      fprintf(stderr, "Error sending response\n");
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-socket-meaning.c b/gcc/testsuite/gcc.dg/analyzer/fd-socket-meaning.c
new file mode 100644
index 00000000000..5bfb57f68fb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-socket-meaning.c
@@ -0,0 +1,21 @@ 
+/* { dg-additional-options "-fanalyzer-verbose-state-changes" } */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+
+void test_leak_unchecked_stream_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "meaning: \\{verb: 'acquire', noun: 'resource'\\}" } */
+} /* { dg-warning "leak of file descriptor 'fd'" } */
+
+void test_leak_unchecked_datagram_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_DGRAM, 0); /* { dg-message "meaning: \\{verb: 'acquire', noun: 'resource'\\}" } */
+} /* { dg-warning "leak of file descriptor 'fd'" } */
+
+void test_leak_unchecked_socket (int type)
+{
+  int fd = socket (AF_UNIX, type, 0); /* { dg-message "meaning: \\{verb: 'acquire', noun: 'resource'\\}" } */
+} /* { dg-warning "leak of file descriptor 'fd'" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-socket-misuse.c b/gcc/testsuite/gcc.dg/analyzer/fd-socket-misuse.c
new file mode 100644
index 00000000000..4ff08d5ec19
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-socket-misuse.c
@@ -0,0 +1,98 @@ 
+/* Various operations done on sockets in the wrong phase.  */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+void test_read_on_new_socket (void *buf)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */
+  if (fd == -1)
+    return;
+  read (fd, buf, 1); /* { dg-warning "'read' on file descriptor 'fd' in wrong phase \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */
+  /* { dg-message "'read' expects a stream socket to be connected via 'accept' but 'fd' has not yet been bound" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+void test_read_on_bound_socket (int fd, const char *sockname, void *buf)
+{
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  if (bind (fd, (struct sockaddr *)&addr, sizeof (addr)) == -1)
+    return;
+  /* This could be a datagram socket, so we shouldn't complain here.  */
+  read (fd, buf, 1);
+}
+
+void test_read_on_listening_socket (int fd, void *buf)
+{
+  if (listen (fd, 5) == -1) /* { dg-message "stream socket marked as passive here via 'listen'" } */
+    return;
+  read (fd, buf, 1); /* { dg-message "'read' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'read' expects a stream socket to be connected via the return value of 'accept' but 'fd' is listening; wrong file descriptor\\\?" "final event" { target *-*-* } .-1 } */
+}
+
+void test_bind_on_non_socket (const char *filename, const char *sockname)
+{
+  int fd = open (filename, O_RDONLY);
+  if (fd == -1)
+    return;
+
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  int result = bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on non-socket file descriptor 'fd' \\\[-Wanalyzer-fd-type-mismatch\\\]" "warning" } */
+  /* { dg-message "'bind' expects a socket file descriptor but 'fd' is not a socket" "final event" { target *-*-* } .-1 } */
+  __analyzer_eval (result == -1); /* { dg-warning "TRUE" } */
+  
+  close (fd);
+}
+
+void test_passive_open_read_on_wrong_socket (int sfd)
+{
+  int cfd = accept (sfd, NULL, NULL);
+  write (sfd, "hello", 6); /* { dg-warning "'write' on file descriptor 'sfd' in wrong phase" "warning" } */
+  /* { dg-message "'write' expects a stream socket to be connected via the return value of 'accept' but 'sfd' is listening; wrong file descriptor\\\?" "final event" { target *-*-* } .-1 } */
+  close (cfd);
+}
+
+void test_listen_on_new_stream_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1)
+    return;
+  listen (fd, 5); /* { dg-message "'listen' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'listen' expects a bound stream socket file descriptor but 'fd' has not yet been bound" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+void test_accept_on_new_stream_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1)
+    return;
+  accept (fd, NULL, NULL); /* { dg-message "'accept' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'accept' expects a listening stream socket file descriptor but 'fd' has not yet been bound" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+void test_listen_before_bind (int fd, const char *sockname)
+{
+  if (listen (fd, 5) == -1) /* { dg-message "stream socket marked as passive here via 'listen'" } */
+    return;
+
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'bind' expects a new socket file descriptor but 'fd' is already listening" "final event" { target *-*-* } .-1 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-active-open.c b/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-active-open.c
new file mode 100644
index 00000000000..7fde0ef6285
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-active-open.c
@@ -0,0 +1,74 @@ 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+void test_active_open_from_scratch (const char *sockname, void *buf)
+{
+  errno = 0;
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-invalid'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+
+  errno = 0;
+  if (connect (fd, (struct sockaddr *)&addr, sizeof (addr)) == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-stream-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-connected-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+
+  write (fd, "hello", 6);
+  read (fd, buf, 100);
+
+  close (fd);
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+}
+
+void test_active_open_from_connect (int fd, const char *sockname, void *buf)
+{
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'start'" } */
+
+  struct sockaddr_un addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+
+  errno = 0;
+  if (connect (fd, (struct sockaddr *)&addr, sizeof (addr)) == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-unknown-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-stop'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+
+  write (fd, "hello", 6);
+  read (fd, buf, 100);
+
+  close (fd);
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-stop'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-passive-open.c b/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-passive-open.c
new file mode 100644
index 00000000000..c31e5b5eefb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-passive-open.c
@@ -0,0 +1,197 @@ 
+/* Verify the various states when performing a passive open,
+   either from scratch, or when various phases are assumed to already
+   be done.  */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+void test_passive_open_from_scratch (const char *sockname, void *buf)
+{
+  struct sockaddr_un addr;
+  int afd;
+  errno = 0;
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-invalid'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  errno = 0;
+  if (bind (fd, (struct sockaddr *)&addr, sizeof (addr)) == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-stream-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-bound-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  if (listen (fd, 5) == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-bound-stream-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  afd = accept (fd, NULL, NULL);
+  if (afd == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+  __analyzer_dump_state ("file-descriptor", afd); /* { dg-warning "state: 'fd-connected-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (afd >= 0); /* { dg-warning "TRUE" } */
+
+  write (afd, "hello", 6);
+  read (afd, buf, 100);
+  
+  close (afd);
+  close (fd);
+  __analyzer_dump_state ("file-descriptor", afd); /* { dg-warning "state: 'fd-closed'" } */
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+}
+
+void test_passive_open_from_bind (int fd, const char *sockname, void *buf)
+{
+  struct sockaddr_un addr;
+  int afd;
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'start'" } */
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  errno = 0;
+  if (bind (fd, (struct sockaddr *)&addr, sizeof (addr)) == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-new-unknown-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-bound-unknown-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  if (listen (fd, 5) == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-bound-unknown-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  afd = accept (fd, NULL, NULL);
+  if (afd == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+  __analyzer_dump_state ("file-descriptor", afd); /* { dg-warning "state: 'fd-connected-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (afd >= 0); /* { dg-warning "TRUE" } */
+
+  write (afd, "hello", 6);  
+  read (afd, buf, 100);
+
+  close (afd);
+  close (fd);
+  __analyzer_dump_state ("file-descriptor", afd); /* { dg-warning "state: 'fd-closed'" } */
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+}
+
+void test_passive_open_from_listen (int fd, void *buf)
+{
+  int afd;
+  errno = 0;
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'start'" } */
+  if (listen (fd, 5) == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-bound-stream-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  afd = accept (fd, NULL, NULL);
+  if (afd == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+  __analyzer_dump_state ("file-descriptor", afd); /* { dg-warning "state: 'fd-connected-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (afd >= 0); /* { dg-warning "TRUE" } */
+
+  write (afd, "hello", 6);
+  read (afd, buf, 100);
+
+  close (afd);
+  close (fd);
+  __analyzer_dump_state ("file-descriptor", afd); /* { dg-warning "state: 'fd-closed'" } */
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+}
+
+void test_passive_open_from_accept (int fd, void *buf)
+{
+  int afd;
+  errno = 0;
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'start'" } */
+  afd = accept (fd, NULL, NULL);
+  if (afd == -1)
+    {
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+      __analyzer_eval (errno > 0); /* { dg-warning "TRUE" } */
+      close (fd);
+      __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+      return;
+    }
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-listening-stream-socket'" } */
+  __analyzer_dump_state ("file-descriptor", afd); /* { dg-warning "state: 'fd-connected-stream-socket'" } */
+  __analyzer_eval (errno == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fd >= 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (afd >= 0); /* { dg-warning "TRUE" } */
+
+  write (afd, "hello", 6);
+  read (afd, buf, 100);
+
+  close (afd);
+  close (fd);
+  __analyzer_dump_state ("file-descriptor", afd); /* { dg-warning "state: 'fd-closed'" } */
+  __analyzer_dump_state ("file-descriptor", fd); /* { dg-warning "state: 'fd-closed'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket.c b/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket.c
new file mode 100644
index 00000000000..3a292d0e2d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-stream-socket.c
@@ -0,0 +1,98 @@ 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+void test_leak_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */
+} /* { dg-warning "leak of file descriptor 'fd'" } */
+
+void test_leak_socket_no_lhs (void)
+{
+  socket (AF_UNIX, SOCK_STREAM, 0);  /* { dg-warning "leak of file descriptor" } */
+}
+
+void test_close_unchecked_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  close (fd);
+}
+
+void test_close_checked_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1)
+    return;
+  close (fd);
+}
+
+void test_leak_checked_socket (void)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */
+  if (fd == -1) /* { dg-warning "leak of file descriptor 'fd'" } */
+    return;
+  // TODO: strange location for leak message
+}
+
+void test_bind_on_checked_socket (const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1)
+    return;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr));
+  close (fd);
+}
+
+void test_bind_on_unchecked_socket (const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "when 'socket' fails" } */
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on possibly invalid file descriptor 'fd'" } */
+  close (fd);
+}
+
+void test_leak_of_bound_socket (const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */
+  if (fd == -1)
+    return;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "leak of file descriptor 'fd'" } */
+}
+
+void test_listen_without_bind (void)
+{
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */
+  if (fd == -1)
+    return;
+  listen (fd, 5); /* { dg-warning "'listen' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'listen' expects a bound stream socket file descriptor but 'fd' has not yet been bound" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+void test_listen_on_unchecked_bind (const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */
+  if (fd == -1)
+    return;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-message "when 'bind' fails" } */
+  listen (fd, 5); /* { dg-warning "'listen' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'listen' expects a bound stream socket file descriptor but 'fd' has not yet been bound" "final event" { target *-*-* } .-1 } */
+  close (fd);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-symbolic-socket.c b/gcc/testsuite/gcc.dg/analyzer/fd-symbolic-socket.c
new file mode 100644
index 00000000000..83400c18f50
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-symbolic-socket.c
@@ -0,0 +1,98 @@ 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "analyzer-decls.h"
+
+void test_leak_socket (int type)
+{
+  int fd = socket (AF_UNIX, type, 0); /* { dg-message "socket created here" } */
+} /* { dg-warning "leak of file descriptor 'fd'" } */
+
+void test_leak_socket_no_lhs (int type)
+{
+  socket (AF_UNIX, type, 0);  /* { dg-warning "leak of file descriptor" } */
+}
+
+void test_close_unchecked_socket (int type)
+{
+  int fd = socket (AF_UNIX, type, 0);
+  close (fd);
+}
+
+void test_close_checked_socket (int type)
+{
+  int fd = socket (AF_UNIX, type, 0);
+  if (fd == -1)
+    return;
+  close (fd);
+}
+
+void test_leak_checked_socket (int type)
+{
+  int fd = socket (AF_UNIX, type, 0); /* { dg-message "socket created here" } */
+  if (fd == -1) /* { dg-warning "leak of file descriptor 'fd'" } */
+    return;
+  // TODO: strange location for leak message
+}
+
+void test_bind_on_checked_socket (int type, const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, type, 0);
+  if (fd == -1)
+    return;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr));
+  close (fd);
+}
+
+void test_bind_on_unchecked_socket (int type, const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, type, 0); /* { dg-message "when 'socket' fails" } */
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on possibly invalid file descriptor 'fd'" } */
+  close (fd);
+}
+
+void test_leak_of_bound_socket (int type, const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, type, 0); /* { dg-message "socket created here" } */
+  if (fd == -1)
+    return;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "leak of file descriptor 'fd'" } */
+}
+
+void test_listen_without_bind (int type)
+{
+  int fd = socket (AF_UNIX, type, 0);
+  if (fd == -1)
+    return;
+  listen (fd, 5); /* { dg-warning "'listen' on file descriptor 'fd' in wrong phase" } */
+  /* { dg-message "'listen' expects a bound stream socket file descriptor but 'fd' has not yet been bound" "msg" { target *-*-* } .-1 } */
+  close (fd);
+}
+
+void test_listen_on_unchecked_bind (int type, const char *sockname)
+{
+  struct sockaddr_un addr;
+  int fd = socket (AF_UNIX, type, 0);
+  if (fd == -1)
+    return;
+  memset (&addr, 0, sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
+  bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-message "when 'bind' fails" } */
+  listen (fd, 5); /* { dg-warning "'listen' on file descriptor 'fd' in wrong phase" "warning" } */
+  /* { dg-message "'listen' expects a bound stream socket file descriptor but 'fd' has not yet been bound" "msg" { target *-*-* } .-1 } */
+  close (fd);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr104369-1.c b/gcc/testsuite/gcc.dg/analyzer/pr104369-1.c
index d3b32418a9e..c05137bb219 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr104369-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr104369-1.c
@@ -1,5 +1,5 @@ 
-/* { dg-additional-options "--param analyzer-max-enodes-per-program-point=10" } */
-// TODO: remove need for this option
+/* { dg-additional-options "-Wno-analyzer-too-complex -Wno-analyzer-fd-leak" } */
+// TODO: remove need for these options
 
 typedef __SIZE_TYPE__ size_t;
 #define NULL ((void *)0)
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr104369-2.c b/gcc/testsuite/gcc.dg/analyzer/pr104369-2.c
index 57dc9caf3e9..93d9987d0ba 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr104369-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr104369-2.c
@@ -1,3 +1,6 @@ 
+/* { dg-additional-options "-Wno-analyzer-fd-leak" } */
+// TODO: remove need for this option
+
 typedef __SIZE_TYPE__ size_t;
 #define NULL ((void *)0)
 #define POLLIN 0x001