libpq: Use modern socket flags, if available.
authorThomas Munro <tmunro@postgresql.org>
Fri, 17 Mar 2023 04:08:02 +0000 (17:08 +1300)
committerThomas Munro <tmunro@postgresql.org>
Fri, 17 Mar 2023 07:40:34 +0000 (20:40 +1300)
Since commit 7627b91cd5d, libpq has used FD_CLOEXEC so that sockets
wouldn't be leaked to subprograms.  With enough bad luck, a
multi-threaded program might fork in between the socket() and fcntl()
calls.  We can close that tiny gap by using SOCK_CLOEXEC instead of a
separate call.  While here, we might as well do the same for
SOCK_NONBLOCK, to save another syscall.

These flags are expected to appear in the next revision of the POSIX
standard, specifically to address this problem.  Our Unixoid targets
except macOS and AIX have had them for a long time, and macOS would
hopefully use guarded availability to roll them out, so it seems enough
to use a simple ifdef test for availability until we hear otherwise.
Windows doesn't have them, but has non-inheritable sockets by default.

Discussion: https://postgr.es/m/CA%2BhUKGKb6FsAdQWcRL35KJsftv%2B9zXqQbzwkfRf1i0J2e57%2BhQ%40mail.gmail.com

src/interfaces/libpq/fe-connect.c

index 0c197589ab912b5704eb244367823063c70d8954..b9f899c552eef99421a654353d2a7fceeb7ccf80 100644 (file)
@@ -2629,6 +2629,7 @@ keep_going:                       /* We will come back to here until there is
                {
                    struct addrinfo *addr_cur = conn->addr_cur;
                    char        host_addr[NI_MAXHOST];
+                   int         sock_type;
 
                    /*
                     * Advance to next possible host, if we've tried all of
@@ -2659,7 +2660,26 @@ keep_going:                      /* We will come back to here until there is
                        conn->connip = strdup(host_addr);
 
                    /* Try to create the socket */
-                   conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
+                   sock_type = SOCK_STREAM;
+#ifdef SOCK_CLOEXEC
+
+                   /*
+                    * Atomically mark close-on-exec, if possible on this
+                    * platform, so that there isn't a window where a
+                    * subprogram executed by another thread inherits the
+                    * socket.  See fallback code below.
+                    */
+                   sock_type |= SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+
+                   /*
+                    * We might as well skip a system call for nonblocking
+                    * mode too, if we can.
+                    */
+                   sock_type |= SOCK_NONBLOCK;
+#endif
+                   conn->sock = socket(addr_cur->ai_family, sock_type, 0);
                    if (conn->sock == PGINVALID_SOCKET)
                    {
                        int         errorno = SOCK_ERRNO;
@@ -2705,6 +2725,7 @@ keep_going:                       /* We will come back to here until there is
                            goto keep_going;
                        }
                    }
+#ifndef SOCK_NONBLOCK
                    if (!pg_set_noblock(conn->sock))
                    {
                        libpq_append_conn_error(conn, "could not set socket to nonblocking mode: %s",
@@ -2712,7 +2733,9 @@ keep_going:                       /* We will come back to here until there is
                        conn->try_next_addr = true;
                        goto keep_going;
                    }
+#endif
 
+#ifndef SOCK_CLOEXEC
 #ifdef F_SETFD
                    if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1)
                    {
@@ -2722,6 +2745,7 @@ keep_going:                       /* We will come back to here until there is
                        goto keep_going;
                    }
 #endif                         /* F_SETFD */
+#endif
 
                    if (addr_cur->ai_family != AF_UNIX)
                    {