Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b5ebef7

Browse files
committed
Push enable/disable of notify and catchup interrupts all the way down
to just around the bare recv() call that gets a command from the client. The former placement in PostgresMain was unsafe because the intermediate processing layers (especially SSL) use facilities such as malloc that are not necessarily re-entrant. Per report from counterstorm.com.
1 parent 8dfb616 commit b5ebef7

File tree

3 files changed

+142
-24
lines changed

3 files changed

+142
-24
lines changed

src/backend/libpq/be-secure.c

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.56 2005/01/08 22:51:12 tgl Exp $
14+
* $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.57 2005/06/02 21:03:17 tgl Exp $
1515
*
1616
* Since the server static private key ($DataDir/server.key)
1717
* will normally be stored unencrypted so that the database
@@ -79,7 +79,6 @@
7979
#include <sys/stat.h>
8080
#include <signal.h>
8181
#include <fcntl.h>
82-
#include <errno.h>
8382
#include <ctype.h>
8483
#include <sys/socket.h>
8584
#include <unistd.h>
@@ -97,6 +96,8 @@
9796

9897
#include "libpq/libpq.h"
9998
#include "miscadmin.h"
99+
#include "tcop/tcopprot.h"
100+
100101

101102
#ifdef USE_SSL
102103
static DH *load_dh_file(int keylength);
@@ -308,8 +309,14 @@ secure_read(Port *port, void *ptr, size_t len)
308309
}
309310
else
310311
#endif
312+
{
313+
prepare_for_client_read();
314+
311315
n = recv(port->sock, ptr, len, 0);
312316

317+
client_read_ended();
318+
}
319+
313320
return n;
314321
}
315322

@@ -410,6 +417,73 @@ secure_write(Port *port, void *ptr, size_t len)
410417
/* SSL specific code */
411418
/* ------------------------------------------------------------ */
412419
#ifdef USE_SSL
420+
421+
/*
422+
* Private substitute BIO: this wraps the SSL library's standard socket BIO
423+
* so that we can enable and disable interrupts just while calling recv().
424+
* We cannot have interrupts occurring while the bulk of openssl runs,
425+
* because it uses malloc() and possibly other non-reentrant libc facilities.
426+
*
427+
* As of openssl 0.9.7, we can use the reasonably clean method of interposing
428+
* a wrapper around the standard socket BIO's sock_read() method. This relies
429+
* on the fact that sock_read() doesn't call anything non-reentrant, in fact
430+
* not much of anything at all except recv(). If this ever changes we'd
431+
* probably need to duplicate the code of sock_read() in order to push the
432+
* interrupt enable/disable down yet another level.
433+
*/
434+
435+
static bool my_bio_initialized = false;
436+
static BIO_METHOD my_bio_methods;
437+
static int (*std_sock_read) (BIO *h, char *buf, int size);
438+
439+
static int
440+
my_sock_read(BIO *h, char *buf, int size)
441+
{
442+
int res;
443+
444+
prepare_for_client_read();
445+
446+
res = std_sock_read(h, buf, size);
447+
448+
client_read_ended();
449+
450+
return res;
451+
}
452+
453+
static BIO_METHOD *
454+
my_BIO_s_socket(void)
455+
{
456+
if (!my_bio_initialized)
457+
{
458+
memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
459+
std_sock_read = my_bio_methods.bread;
460+
my_bio_methods.bread = my_sock_read;
461+
my_bio_initialized = true;
462+
}
463+
return &my_bio_methods;
464+
}
465+
466+
/* This should exactly match openssl's SSL_set_fd except for using my BIO */
467+
static int
468+
my_SSL_set_fd(SSL *s, int fd)
469+
{
470+
int ret=0;
471+
BIO *bio=NULL;
472+
473+
bio=BIO_new(my_BIO_s_socket());
474+
475+
if (bio == NULL)
476+
{
477+
SSLerr(SSL_F_SSL_SET_FD,ERR_R_BUF_LIB);
478+
goto err;
479+
}
480+
BIO_set_fd(bio,fd,BIO_NOCLOSE);
481+
SSL_set_bio(s,bio,bio);
482+
ret=1;
483+
err:
484+
return(ret);
485+
}
486+
413487
/*
414488
* Load precomputed DH parameters.
415489
*
@@ -761,7 +835,7 @@ open_server_SSL(Port *port)
761835
close_SSL(port);
762836
return -1;
763837
}
764-
if (!SSL_set_fd(port->ssl, port->sock))
838+
if (!my_SSL_set_fd(port->ssl, port->sock))
765839
{
766840
ereport(COMMERROR,
767841
(errcode(ERRCODE_PROTOCOL_VIOLATION),

src/backend/tcop/postgres.c

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.445 2005/06/01 23:27:03 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.446 2005/06/02 21:03:24 tgl Exp $
1212
*
1313
* NOTES
1414
* this is the "main" module of the postgres backend and
@@ -111,6 +111,13 @@ static volatile sig_atomic_t got_SIGHUP = false;
111111
*/
112112
static bool xact_started = false;
113113

114+
/*
115+
* Flag to indicate that we are doing the outer loop's read-from-client,
116+
* as opposed to any random read from client that might happen within
117+
* commands like COPY FROM STDIN.
118+
*/
119+
static bool DoingCommandRead = false;
120+
114121
/*
115122
* Flags to implement skip-till-Sync-after-error behavior for messages of
116123
* the extended query protocol.
@@ -406,6 +413,52 @@ ReadCommand(StringInfo inBuf)
406413
return result;
407414
}
408415

416+
/*
417+
* prepare_for_client_read -- set up to possibly block on client input
418+
*
419+
* This must be called immediately before any low-level read from the
420+
* client connection. It is necessary to do it at a sufficiently low level
421+
* that there won't be any other operations except the read kernel call
422+
* itself between this call and the subsequent client_read_ended() call.
423+
* In particular there mustn't be use of malloc() or other potentially
424+
* non-reentrant libc functions. This restriction makes it safe for us
425+
* to allow interrupt service routines to execute nontrivial code while
426+
* we are waiting for input.
427+
*/
428+
void
429+
prepare_for_client_read(void)
430+
{
431+
if (DoingCommandRead)
432+
{
433+
/* Enable immediate processing of asynchronous signals */
434+
EnableNotifyInterrupt();
435+
EnableCatchupInterrupt();
436+
437+
/* Allow "die" interrupt to be processed while waiting */
438+
ImmediateInterruptOK = true;
439+
440+
/* And don't forget to detect one that already arrived */
441+
QueryCancelPending = false;
442+
CHECK_FOR_INTERRUPTS();
443+
}
444+
}
445+
446+
/*
447+
* client_read_ended -- get out of the client-input state
448+
*/
449+
void
450+
client_read_ended(void)
451+
{
452+
if (DoingCommandRead)
453+
{
454+
ImmediateInterruptOK = false;
455+
QueryCancelPending = false; /* forget any CANCEL signal */
456+
457+
DisableNotifyInterrupt();
458+
DisableCatchupInterrupt();
459+
}
460+
}
461+
409462

410463
/*
411464
* Parse a query string and pass it through the rewriter.
@@ -2959,6 +3012,7 @@ PostgresMain(int argc, char *argv[], const char *username)
29593012
* not in other exception-catching places since these interrupts
29603013
* are only enabled while we wait for client input.
29613014
*/
3015+
DoingCommandRead = false;
29623016
DisableNotifyInterrupt();
29633017
DisableCatchupInterrupt();
29643018

@@ -3063,21 +3117,13 @@ PostgresMain(int argc, char *argv[], const char *username)
30633117
}
30643118

30653119
/*
3066-
* (2) deal with pending asynchronous NOTIFY from other backends,
3067-
* and enable async.c's signal handler to execute NOTIFY directly.
3068-
* Then set up other stuff needed before blocking for input.
3120+
* (2) Allow asynchronous signals to be executed immediately
3121+
* if they come in while we are waiting for client input.
3122+
* (This must be conditional since we don't want, say, reads on
3123+
* behalf of COPY FROM STDIN doing the same thing.)
30693124
*/
3070-
QueryCancelPending = false; /* forget any earlier CANCEL
3071-
* signal */
3072-
3073-
EnableNotifyInterrupt();
3074-
EnableCatchupInterrupt();
3075-
3076-
/* Allow "die" interrupt to be processed while waiting */
3077-
ImmediateInterruptOK = true;
3078-
/* and don't forget to detect one that already arrived */
3079-
QueryCancelPending = false;
3080-
CHECK_FOR_INTERRUPTS();
3125+
QueryCancelPending = false; /* forget any earlier CANCEL signal */
3126+
DoingCommandRead = true;
30813127

30823128
/*
30833129
* (3) read a command (loop blocks here)
@@ -3087,11 +3133,7 @@ PostgresMain(int argc, char *argv[], const char *username)
30873133
/*
30883134
* (4) disable async signal conditions again.
30893135
*/
3090-
ImmediateInterruptOK = false;
3091-
QueryCancelPending = false; /* forget any CANCEL signal */
3092-
3093-
DisableNotifyInterrupt();
3094-
DisableCatchupInterrupt();
3136+
DoingCommandRead = false;
30953137

30963138
/*
30973139
* (5) check for any other interesting events that happened while

src/include/tcop/tcopprot.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.73 2004/12/31 22:03:44 pgsql Exp $
10+
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.74 2005/06/02 21:03:25 tgl Exp $
1111
*
1212
* OLD COMMENTS
1313
* This file was created so that other c files could get the two
@@ -59,6 +59,8 @@ extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);
5959
extern void die(SIGNAL_ARGS);
6060
extern void quickdie(SIGNAL_ARGS);
6161
extern void authdie(SIGNAL_ARGS);
62+
extern void prepare_for_client_read(void);
63+
extern void client_read_ended(void);
6264
extern int PostgresMain(int argc, char *argv[], const char *username);
6365
extern void ResetUsage(void);
6466
extern void ShowUsage(const char *title);

0 commit comments

Comments
 (0)