Skip to content

Commit cea80e7

Browse files
committed
Avoid extra system calls to block SIGPIPE if the platform provides either
sockopt(SO_NOSIGPIPE) or the MSG_NOSIGNAL flag to send(). We assume these features are available if (1) the symbol is defined at compile time and (2) the kernel doesn't reject the call at runtime. It might turn out that there are some platforms where (1) and (2) are true and yet the signal isn't really blocked, in which case applications would die on server crash. If that sort of thing gets reported, then we'll have to add additional defenses of some kind. Jeremy Kerr
1 parent 655473a commit cea80e7

File tree

3 files changed

+151
-40
lines changed

3 files changed

+151
-40
lines changed

src/interfaces/libpq/fe-connect.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.375 2009/06/11 14:49:13 momjian Exp $
11+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.376 2009/07/24 17:58:31 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1018,6 +1018,7 @@ PQconnectPoll(PGconn *conn)
10181018
{
10191019
PGresult *res;
10201020
char sebuf[256];
1021+
int optval;
10211022

10221023
if (conn == NULL)
10231024
return PGRES_POLLING_FAILED;
@@ -1153,6 +1154,46 @@ PQconnectPoll(PGconn *conn)
11531154
}
11541155
#endif /* F_SETFD */
11551156

1157+
/*----------
1158+
* We have three methods of blocking SIGPIPE during
1159+
* send() calls to this socket:
1160+
*
1161+
* - setsockopt(sock, SO_NOSIGPIPE)
1162+
* - send(sock, ..., MSG_NOSIGNAL)
1163+
* - setting the signal mask to SIG_IGN during send()
1164+
*
1165+
* The third method requires three syscalls per send,
1166+
* so we prefer either of the first two, but they are
1167+
* less portable. The state is tracked in the following
1168+
* members of PGconn:
1169+
*
1170+
* conn->sigpipe_so - we have set up SO_NOSIGPIPE
1171+
* conn->sigpipe_flag - we're specifying MSG_NOSIGNAL
1172+
*
1173+
* If we can use SO_NOSIGPIPE, then set sigpipe_so here
1174+
* and we're done. Otherwise, set sigpipe_flag so that
1175+
* we will try MSG_NOSIGNAL on sends. If we get an error
1176+
* with MSG_NOSIGNAL, we'll clear that flag and revert to
1177+
* signal masking.
1178+
*----------
1179+
*/
1180+
conn->sigpipe_so = false;
1181+
#ifdef MSG_NOSIGNAL
1182+
conn->sigpipe_flag = true;
1183+
#else
1184+
conn->sigpipe_flag = false;
1185+
#endif /* MSG_NOSIGNAL */
1186+
1187+
#ifdef SO_NOSIGPIPE
1188+
optval = 1;
1189+
if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE,
1190+
(char *) &optval, sizeof(optval)) == 0)
1191+
{
1192+
conn->sigpipe_so = true;
1193+
conn->sigpipe_flag = false;
1194+
}
1195+
#endif /* SO_NOSIGPIPE */
1196+
11561197
/*
11571198
* Start/make connection. This should not block, since we
11581199
* are in nonblock mode. If it does, well, too bad.
@@ -1214,7 +1255,6 @@ PQconnectPoll(PGconn *conn)
12141255

12151256
case CONNECTION_STARTED:
12161257
{
1217-
int optval;
12181258
ACCEPT_TYPE_ARG3 optlen = sizeof(optval);
12191259

12201260
/*

src/interfaces/libpq/fe-secure.c

Lines changed: 106 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.127 2009/06/23 18:13:23 mha Exp $
14+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.128 2009/07/24 17:58:31 tgl Exp $
1515
*
1616
* NOTES
1717
*
@@ -118,44 +118,76 @@ static long win32_ssl_create_mutex = 0;
118118

119119
/*
120120
* Macros to handle disabling and then restoring the state of SIGPIPE handling.
121-
* Note that DISABLE_SIGPIPE() must appear at the start of a block.
121+
* On Windows, these are all no-ops since there's no SIGPIPEs.
122122
*/
123123

124124
#ifndef WIN32
125+
126+
#define SIGPIPE_MASKED(conn) ((conn)->sigpipe_so || (conn)->sigpipe_flag)
127+
125128
#ifdef ENABLE_THREAD_SAFETY
126129

127-
#define DISABLE_SIGPIPE(failaction) \
128-
sigset_t osigmask; \
129-
bool sigpipe_pending; \
130-
bool got_epipe = false; \
131-
\
132-
if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0) \
133-
failaction
130+
struct sigpipe_info
131+
{
132+
sigset_t oldsigmask;
133+
bool sigpipe_pending;
134+
bool got_epipe;
135+
};
134136

135-
#define REMEMBER_EPIPE(cond) \
137+
#define DECLARE_SIGPIPE_INFO(spinfo) struct sigpipe_info spinfo
138+
139+
#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
140+
do { \
141+
(spinfo).got_epipe = false; \
142+
if (!SIGPIPE_MASKED(conn)) \
143+
{ \
144+
if (pq_block_sigpipe(&(spinfo).oldsigmask, \
145+
&(spinfo).sigpipe_pending) < 0) \
146+
failaction; \
147+
} \
148+
} while (0)
149+
150+
#define REMEMBER_EPIPE(spinfo, cond) \
136151
do { \
137152
if (cond) \
138-
got_epipe = true; \
153+
(spinfo).got_epipe = true; \
139154
} while (0)
140155

141-
#define RESTORE_SIGPIPE() \
142-
pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe)
143-
#else /* !ENABLE_THREAD_SAFETY */
156+
#define RESTORE_SIGPIPE(conn, spinfo) \
157+
do { \
158+
if (!SIGPIPE_MASKED(conn)) \
159+
pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \
160+
(spinfo).got_epipe); \
161+
} while (0)
144162

145-
#define DISABLE_SIGPIPE(failaction) \
146-
pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN)
163+
#else /* !ENABLE_THREAD_SAFETY */
147164

148-
#define REMEMBER_EPIPE(cond)
165+
#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL
149166

150-
#define RESTORE_SIGPIPE() \
151-
pqsignal(SIGPIPE, oldsighandler)
152-
#endif /* ENABLE_THREAD_SAFETY */
153-
#else /* WIN32 */
167+
#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
168+
do { \
169+
if (!SIGPIPE_MASKED(conn)) \
170+
spinfo = pqsignal(SIGPIPE, SIG_IGN); \
171+
} while (0)
172+
173+
#define REMEMBER_EPIPE(spinfo, cond)
174+
175+
#define RESTORE_SIGPIPE(conn, spinfo) \
176+
do { \
177+
if (!SIGPIPE_MASKED(conn)) \
178+
pqsignal(SIGPIPE, spinfo); \
179+
} while (0)
180+
181+
#endif /* ENABLE_THREAD_SAFETY */
154182

155-
#define DISABLE_SIGPIPE(failaction)
156-
#define REMEMBER_EPIPE(cond)
157-
#define RESTORE_SIGPIPE()
158-
#endif /* WIN32 */
183+
#else /* WIN32 */
184+
185+
#define DECLARE_SIGPIPE_INFO(spinfo)
186+
#define DISABLE_SIGPIPE(conn, spinfo, failaction)
187+
#define REMEMBER_EPIPE(spinfo, cond)
188+
#define RESTORE_SIGPIPE(conn, spinfo)
189+
190+
#endif /* WIN32 */
159191

160192
/* ------------------------------------------------------------ */
161193
/* Procedures common to all secure sessions */
@@ -231,6 +263,9 @@ pqsecure_open_client(PGconn *conn)
231263
/* First time through? */
232264
if (conn->ssl == NULL)
233265
{
266+
/* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
267+
conn->sigpipe_flag = false;
268+
234269
if (!(conn->ssl = SSL_new(SSL_context)) ||
235270
!SSL_set_app_data(conn->ssl, conn) ||
236271
!SSL_set_fd(conn->ssl, conn->sock))
@@ -283,9 +318,10 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
283318
if (conn->ssl)
284319
{
285320
int err;
321+
DECLARE_SIGPIPE_INFO(spinfo);
286322

287323
/* SSL_read can write to the socket, so we need to disable SIGPIPE */
288-
DISABLE_SIGPIPE(return -1);
324+
DISABLE_SIGPIPE(conn, spinfo, return -1);
289325

290326
rloop:
291327
n = SSL_read(conn->ssl, ptr, len);
@@ -312,7 +348,7 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
312348

313349
if (n == -1)
314350
{
315-
REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
351+
REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE);
316352
printfPQExpBuffer(&conn->errorMessage,
317353
libpq_gettext("SSL SYSCALL error: %s\n"),
318354
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
@@ -348,7 +384,7 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
348384
break;
349385
}
350386

351-
RESTORE_SIGPIPE();
387+
RESTORE_SIGPIPE(conn, spinfo);
352388
}
353389
else
354390
#endif
@@ -364,14 +400,15 @@ ssize_t
364400
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
365401
{
366402
ssize_t n;
367-
368-
DISABLE_SIGPIPE(return -1);
403+
DECLARE_SIGPIPE_INFO(spinfo);
369404

370405
#ifdef USE_SSL
371406
if (conn->ssl)
372407
{
373408
int err;
374409

410+
DISABLE_SIGPIPE(conn, spinfo, return -1);
411+
375412
n = SSL_write(conn->ssl, ptr, len);
376413
err = SSL_get_error(conn->ssl, n);
377414
switch (err)
@@ -396,7 +433,7 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
396433

397434
if (n == -1)
398435
{
399-
REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
436+
REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE);
400437
printfPQExpBuffer(&conn->errorMessage,
401438
libpq_gettext("SSL SYSCALL error: %s\n"),
402439
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
@@ -434,11 +471,41 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
434471
else
435472
#endif
436473
{
437-
n = send(conn->sock, ptr, len, 0);
438-
REMEMBER_EPIPE(n < 0 && SOCK_ERRNO == EPIPE);
474+
int flags = 0;
475+
476+
#ifdef MSG_NOSIGNAL
477+
if (conn->sigpipe_flag)
478+
flags |= MSG_NOSIGNAL;
479+
480+
retry_masked:
481+
482+
#endif /* MSG_NOSIGNAL */
483+
484+
DISABLE_SIGPIPE(conn, spinfo, return -1);
485+
486+
n = send(conn->sock, ptr, len, flags);
487+
488+
if (n < 0)
489+
{
490+
/*
491+
* If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
492+
* available on this machine. So, clear sigpipe_flag so we don't
493+
* try the flag again, and retry the send().
494+
*/
495+
#ifdef MSG_NOSIGNAL
496+
if (flags != 0 && SOCK_ERRNO == EINVAL)
497+
{
498+
conn->sigpipe_flag = false;
499+
flags = 0;
500+
goto retry_masked;
501+
}
502+
#endif /* MSG_NOSIGNAL */
503+
504+
REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE);
505+
}
439506
}
440507

441-
RESTORE_SIGPIPE();
508+
RESTORE_SIGPIPE(conn, spinfo);
442509

443510
return n;
444511
}
@@ -1220,14 +1287,16 @@ close_SSL(PGconn *conn)
12201287
{
12211288
if (conn->ssl)
12221289
{
1223-
DISABLE_SIGPIPE((void) 0);
1290+
DECLARE_SIGPIPE_INFO(spinfo);
1291+
1292+
DISABLE_SIGPIPE(conn, spinfo, (void) 0);
12241293
SSL_shutdown(conn->ssl);
12251294
SSL_free(conn->ssl);
12261295
conn->ssl = NULL;
12271296
pqsecure_destroy();
12281297
/* We have to assume we got EPIPE */
1229-
REMEMBER_EPIPE(true);
1230-
RESTORE_SIGPIPE();
1298+
REMEMBER_EPIPE(spinfo, true);
1299+
RESTORE_SIGPIPE(conn, spinfo);
12311300
}
12321301

12331302
if (conn->peer)

src/interfaces/libpq/libpq-int.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
1313
* Portions Copyright (c) 1994, Regents of the University of California
1414
*
15-
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.143 2009/06/23 18:13:23 mha Exp $
15+
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.144 2009/07/24 17:58:31 tgl Exp $
1616
*
1717
*-------------------------------------------------------------------------
1818
*/
@@ -341,6 +341,8 @@ struct pg_conn
341341
ProtocolVersion pversion; /* FE/BE protocol version in use */
342342
int sversion; /* server version, e.g. 70401 for 7.4.1 */
343343
bool password_needed; /* true if server demanded a password */
344+
bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */
345+
bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */
344346

345347
/* Transient state needed while establishing connection */
346348
struct addrinfo *addrlist; /* list of possible backend addresses */

0 commit comments

Comments
 (0)