aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2022-11-15 13:53:42 -0500
committerDavid Malcolm <dmalcolm@redhat.com>2022-11-15 13:53:42 -0500
commit86a90006864840c2e222d46ead551850caba184b (patch)
treedbd40ec5eead9974edb997d246a79036eed69d31 /gcc/testsuite
parentd8aba860b34203621586df8c5a6756b18c2a0c32 (diff)
downloadgcc-86a90006864840c2e222d46ead551850caba184b.zip
gcc-86a90006864840c2e222d46ead551850caba184b.tar.gz
gcc-86a90006864840c2e222d46ead551850caba184b.tar.bz2
analyzer: add warnings relating to sockets [PR106140]
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/invoke.texi (Static Analyzer Options): Add -Wanalyzer-fd-phase-mismatch and -Wanalyzer-fd-type-mismatch. Add "socket", "bind", "listen", "accept", and "connect" to the list of functions known to the analyzer. 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>
Diffstat (limited to 'gcc/testsuite')
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-accept.c69
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-bind.c74
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-connect.c46
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-datagram-socket.c108
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-connection-server.c133
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-glibc-byte-stream-socket.c62
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-client.c56
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-glibc-datagram-socket.c52
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-glibc-make_named_socket.h47
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-listen.c63
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-manpage-getaddrinfo-client.c122
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-mappage-getaddrinfo-server.c119
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-socket-meaning.c21
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-socket-misuse.c98
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-active-open.c74
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-stream-socket-passive-open.c197
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-stream-socket.c98
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-symbolic-socket.c98
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr104369-1.c4
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr104369-2.c3
20 files changed, 1542 insertions, 2 deletions
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 0000000..36cc7af
--- /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 0000000..6f91bc4
--- /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 0000000..1ab54d0
--- /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 0000000..045bdfa
--- /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 0000000..1ff9028
--- /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 0000000..f96da81
--- /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 0000000..888c751
--- /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 0000000..b8b6876
--- /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 0000000..bdb6de0
--- /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 0000000..1f54a8f
--- /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 0000000..d9c3ff0
--- /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 0000000..66398e8
--- /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 0000000..5bfb57f
--- /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 0000000..4ff08d5
--- /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 0000000..7fde0ef
--- /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 0000000..c31e5b5
--- /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 0000000..3a292d0
--- /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 0000000..83400c1
--- /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 d3b3241..c05137b 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 57dc9ca..93d9987 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