Arrange to ignore SIGPIPE during SSL_read() and SSL_shutdown(), as these
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 29 Jan 2008 02:03:39 +0000 (02:03 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 29 Jan 2008 02:03:39 +0000 (02:03 +0000)
are known to write on the socket sometimes and thus we are vulnerable to
being killed by the signal if the server happens to go away unexpectedly.
Noticed while trying (futilely) to reproduce bug #3902.

This bug has been there all along, but since the situation is usually
only of interest to developers, I chose not to back-patch the changes.

src/interfaces/libpq/fe-secure.c

index 285f3ea827585968621870535fab9557163b9787..f545eae44cf4011ff86854fabf441cb13ccb82ca 100644 (file)
@@ -162,6 +162,50 @@ static bool pq_initssllib = true;
 static SSL_CTX *SSL_context = NULL;
 #endif
 
+/*
+ * Macros to handle disabling and then restoring the state of SIGPIPE handling.
+ * Note that DISABLE_SIGPIPE() must appear at the start of a block.
+ */
+
+#ifndef WIN32
+#ifdef ENABLE_THREAD_SAFETY
+
+#define DISABLE_SIGPIPE(failaction) \
+       sigset_t        osigmask; \
+       bool            sigpipe_pending; \
+       bool            got_epipe = false; \
+\
+       if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0) \
+               failaction
+
+#define REMEMBER_EPIPE(cond) \
+       do { \
+               if (cond) \
+                       got_epipe = true; \
+       } while (0)
+
+#define RESTORE_SIGPIPE() \
+       pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe)
+
+#else  /* !ENABLE_THREAD_SAFETY */
+
+#define DISABLE_SIGPIPE(failaction) \
+       pqsigfunc       oldsighandler = pqsignal(SIGPIPE, SIG_IGN)
+
+#define REMEMBER_EPIPE(cond)
+
+#define RESTORE_SIGPIPE() \
+       pqsignal(SIGPIPE, oldsighandler)
+
+#endif /* ENABLE_THREAD_SAFETY */
+#else  /* WIN32 */
+
+#define DISABLE_SIGPIPE(failaction)
+#define REMEMBER_EPIPE(cond)
+#define RESTORE_SIGPIPE()
+
+#endif /* WIN32 */
+
 /* ------------------------------------------------------------ */
 /*                      Procedures common to all secure sessions                       */
 /* ------------------------------------------------------------ */
@@ -268,6 +312,9 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
        {
                int                     err;
 
+               /* SSL_read can write to the socket, so we need to disable SIGPIPE */
+               DISABLE_SIGPIPE(return -1);
+
 rloop:
                n = SSL_read(conn->ssl, ptr, len);
                err = SSL_get_error(conn->ssl, n);
@@ -292,9 +339,12 @@ rloop:
                                        char            sebuf[256];
 
                                        if (n == -1)
+                                       {
+                                               REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
                                                printfPQExpBuffer(&conn->errorMessage,
                                                                        libpq_gettext("SSL SYSCALL error: %s\n"),
                                                        SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+                                       }
                                        else
                                        {
                                                printfPQExpBuffer(&conn->errorMessage,
@@ -325,6 +375,8 @@ rloop:
                                n = -1;
                                break;
                }
+
+               RESTORE_SIGPIPE();
        }
        else
 #endif
@@ -341,19 +393,7 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
        ssize_t         n;
 
-#ifndef WIN32
-#ifdef ENABLE_THREAD_SAFETY
-       sigset_t        osigmask;
-       bool            sigpipe_pending;
-       bool            got_epipe = false;
-
-
-       if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0)
-               return -1;
-#else
-       pqsigfunc       oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif   /* ENABLE_THREAD_SAFETY */
-#endif   /* WIN32 */
+       DISABLE_SIGPIPE(return -1);
 
 #ifdef USE_SSL
        if (conn->ssl)
@@ -384,10 +424,7 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 
                                        if (n == -1)
                                        {
-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
-                                               if (SOCK_ERRNO == EPIPE)
-                                                       got_epipe = true;
-#endif
+                                               REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
                                                printfPQExpBuffer(&conn->errorMessage,
                                                                        libpq_gettext("SSL SYSCALL error: %s\n"),
                                                        SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
@@ -426,19 +463,10 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 #endif
        {
                n = send(conn->sock, ptr, len, 0);
-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
-               if (n < 0 && SOCK_ERRNO == EPIPE)
-                       got_epipe = true;
-#endif
+               REMEMBER_EPIPE(n < 0 && SOCK_ERRNO == EPIPE);
        }
 
-#ifndef WIN32
-#ifdef ENABLE_THREAD_SAFETY
-       pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe);
-#else
-       pqsignal(SIGPIPE, oldsighandler);
-#endif   /* ENABLE_THREAD_SAFETY */
-#endif   /* WIN32 */
+       RESTORE_SIGPIPE();
 
        return n;
 }
@@ -1092,9 +1120,13 @@ close_SSL(PGconn *conn)
 {
        if (conn->ssl)
        {
+               DISABLE_SIGPIPE((void) 0);
                SSL_shutdown(conn->ssl);
                SSL_free(conn->ssl);
                conn->ssl = NULL;
+               /* We have to assume we got EPIPE */
+               REMEMBER_EPIPE(true);
+               RESTORE_SIGPIPE();
        }
 
        if (conn->peer)
@@ -1167,6 +1199,7 @@ PQgetssl(PGconn *conn)
 }
 #endif   /* USE_SSL */
 
+
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 
 /*
@@ -1251,4 +1284,4 @@ pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
        SOCK_ERRNO_SET(save_errno);
 }
 
-#endif   /* ENABLE_THREAD_SAFETY */
+#endif   /* ENABLE_THREAD_SAFETY && !WIN32 */