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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy