mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	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.
This commit is contained in:
		@@ -11,7 +11,7 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * IDENTIFICATION
 | 
					 * IDENTIFICATION
 | 
				
			||||||
 *	  $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.43.2.1 2003/12/18 22:49:34 tgl Exp $
 | 
					 *	  $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.43.2.2 2005/06/02 21:04:07 tgl Exp $
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *	  Since the server static private key ($DataDir/server.key)
 | 
					 *	  Since the server static private key ($DataDir/server.key)
 | 
				
			||||||
 *	  will normally be stored unencrypted so that the database
 | 
					 *	  will normally be stored unencrypted so that the database
 | 
				
			||||||
@@ -79,7 +79,6 @@
 | 
				
			|||||||
#include <sys/stat.h>
 | 
					#include <sys/stat.h>
 | 
				
			||||||
#include <signal.h>
 | 
					#include <signal.h>
 | 
				
			||||||
#include <fcntl.h>
 | 
					#include <fcntl.h>
 | 
				
			||||||
#include <errno.h>
 | 
					 | 
				
			||||||
#include <ctype.h>
 | 
					#include <ctype.h>
 | 
				
			||||||
#include <sys/socket.h>
 | 
					#include <sys/socket.h>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
@@ -97,6 +96,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "libpq/libpq.h"
 | 
					#include "libpq/libpq.h"
 | 
				
			||||||
#include "miscadmin.h"
 | 
					#include "miscadmin.h"
 | 
				
			||||||
 | 
					#include "tcop/tcopprot.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_SSL
 | 
					#ifdef USE_SSL
 | 
				
			||||||
static DH  *load_dh_file(int keylength);
 | 
					static DH  *load_dh_file(int keylength);
 | 
				
			||||||
@@ -301,8 +302,14 @@ rloop:
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							prepare_for_client_read();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		n = recv(port->sock, ptr, len, 0);
 | 
							n = recv(port->sock, ptr, len, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							client_read_ended();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return n;
 | 
						return n;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -395,6 +402,73 @@ wloop:
 | 
				
			|||||||
/*						  SSL specific code						*/
 | 
					/*						  SSL specific code						*/
 | 
				
			||||||
/* ------------------------------------------------------------ */
 | 
					/* ------------------------------------------------------------ */
 | 
				
			||||||
#ifdef USE_SSL
 | 
					#ifdef USE_SSL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Private substitute BIO: this wraps the SSL library's standard socket BIO
 | 
				
			||||||
 | 
					 * so that we can enable and disable interrupts just while calling recv().
 | 
				
			||||||
 | 
					 * We cannot have interrupts occurring while the bulk of openssl runs,
 | 
				
			||||||
 | 
					 * because it uses malloc() and possibly other non-reentrant libc facilities.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * As of openssl 0.9.7, we can use the reasonably clean method of interposing
 | 
				
			||||||
 | 
					 * a wrapper around the standard socket BIO's sock_read() method.  This relies
 | 
				
			||||||
 | 
					 * on the fact that sock_read() doesn't call anything non-reentrant, in fact
 | 
				
			||||||
 | 
					 * not much of anything at all except recv().  If this ever changes we'd
 | 
				
			||||||
 | 
					 * probably need to duplicate the code of sock_read() in order to push the
 | 
				
			||||||
 | 
					 * interrupt enable/disable down yet another level.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool my_bio_initialized = false;
 | 
				
			||||||
 | 
					static BIO_METHOD my_bio_methods;
 | 
				
			||||||
 | 
					static int (*std_sock_read) (BIO *h, char *buf, int size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					my_sock_read(BIO *h, char *buf, int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int		res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prepare_for_client_read();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res = std_sock_read(h, buf, size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client_read_ended();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static BIO_METHOD *
 | 
				
			||||||
 | 
					my_BIO_s_socket(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!my_bio_initialized)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
 | 
				
			||||||
 | 
							std_sock_read = my_bio_methods.bread;
 | 
				
			||||||
 | 
							my_bio_methods.bread = my_sock_read;
 | 
				
			||||||
 | 
							my_bio_initialized = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &my_bio_methods;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* This should exactly match openssl's SSL_set_fd except for using my BIO */
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					my_SSL_set_fd(SSL *s, int fd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret=0;
 | 
				
			||||||
 | 
						BIO *bio=NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bio=BIO_new(my_BIO_s_socket());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bio == NULL)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							SSLerr(SSL_F_SSL_SET_FD,ERR_R_BUF_LIB);
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						BIO_set_fd(bio,fd,BIO_NOCLOSE);
 | 
				
			||||||
 | 
						SSL_set_bio(s,bio,bio);
 | 
				
			||||||
 | 
						ret=1;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
						return(ret);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *	Load precomputed DH parameters.
 | 
					 *	Load precomputed DH parameters.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -718,7 +792,7 @@ open_server_SSL(Port *port)
 | 
				
			|||||||
	Assert(!port->peer);
 | 
						Assert(!port->peer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(port->ssl = SSL_new(SSL_context)) ||
 | 
						if (!(port->ssl = SSL_new(SSL_context)) ||
 | 
				
			||||||
		!SSL_set_fd(port->ssl, port->sock) ||
 | 
							!my_SSL_set_fd(port->ssl, port->sock) ||
 | 
				
			||||||
		SSL_accept(port->ssl) <= 0)
 | 
							SSL_accept(port->ssl) <= 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		ereport(COMMERROR,
 | 
							ereport(COMMERROR,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * IDENTIFICATION
 | 
					 * IDENTIFICATION
 | 
				
			||||||
 *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.375.2.2 2004/09/26 00:26:53 tgl Exp $
 | 
					 *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.375.2.3 2005/06/02 21:04:08 tgl Exp $
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * NOTES
 | 
					 * NOTES
 | 
				
			||||||
 *	  this is the "main" module of the postgres backend and
 | 
					 *	  this is the "main" module of the postgres backend and
 | 
				
			||||||
@@ -108,6 +108,13 @@ static volatile bool got_SIGHUP = false;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
static bool xact_started = false;
 | 
					static bool xact_started = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Flag to indicate that we are doing the outer loop's read-from-client,
 | 
				
			||||||
 | 
					 * as opposed to any random read from client that might happen within
 | 
				
			||||||
 | 
					 * commands like COPY FROM STDIN.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static bool DoingCommandRead = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Flags to implement skip-till-Sync-after-error behavior for messages of
 | 
					 * Flags to implement skip-till-Sync-after-error behavior for messages of
 | 
				
			||||||
 * the extended query protocol.
 | 
					 * the extended query protocol.
 | 
				
			||||||
@@ -400,6 +407,50 @@ ReadCommand(StringInfo inBuf)
 | 
				
			|||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * prepare_for_client_read -- set up to possibly block on client input
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This must be called immediately before any low-level read from the
 | 
				
			||||||
 | 
					 * client connection.  It is necessary to do it at a sufficiently low level
 | 
				
			||||||
 | 
					 * that there won't be any other operations except the read kernel call
 | 
				
			||||||
 | 
					 * itself between this call and the subsequent client_read_ended() call.
 | 
				
			||||||
 | 
					 * In particular there mustn't be use of malloc() or other potentially
 | 
				
			||||||
 | 
					 * non-reentrant libc functions.  This restriction makes it safe for us
 | 
				
			||||||
 | 
					 * to allow interrupt service routines to execute nontrivial code while
 | 
				
			||||||
 | 
					 * we are waiting for input.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					prepare_for_client_read(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (DoingCommandRead)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Enable immediate processing of asynchronous signals */
 | 
				
			||||||
 | 
							EnableNotifyInterrupt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Allow "die" interrupt to be processed while waiting */
 | 
				
			||||||
 | 
							ImmediateInterruptOK = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* And don't forget to detect one that already arrived */
 | 
				
			||||||
 | 
							QueryCancelPending = false;
 | 
				
			||||||
 | 
							CHECK_FOR_INTERRUPTS();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * client_read_ended -- get out of the client-input state
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					client_read_ended(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (DoingCommandRead)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ImmediateInterruptOK = false;
 | 
				
			||||||
 | 
							QueryCancelPending = false;		/* forget any CANCEL signal */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DisableNotifyInterrupt();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Parse a query string and pass it through the rewriter.
 | 
					 * Parse a query string and pass it through the rewriter.
 | 
				
			||||||
@@ -2659,7 +2710,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 | 
				
			|||||||
	if (!IsUnderPostmaster)
 | 
						if (!IsUnderPostmaster)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		puts("\nPOSTGRES backend interactive interface ");
 | 
							puts("\nPOSTGRES backend interactive interface ");
 | 
				
			||||||
		puts("$Revision: 1.375.2.2 $ $Date: 2004/09/26 00:26:53 $\n");
 | 
							puts("$Revision: 1.375.2.3 $ $Date: 2005/06/02 21:04:08 $\n");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
@@ -2706,6 +2757,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 | 
				
			|||||||
		CritSectionCount = 0;	/* should be unnecessary, but... */
 | 
							CritSectionCount = 0;	/* should be unnecessary, but... */
 | 
				
			||||||
		disable_sig_alarm(true);
 | 
							disable_sig_alarm(true);
 | 
				
			||||||
		QueryCancelPending = false;		/* again in case timeout occurred */
 | 
							QueryCancelPending = false;		/* again in case timeout occurred */
 | 
				
			||||||
 | 
							DoingCommandRead = false;
 | 
				
			||||||
		DisableNotifyInterrupt();
 | 
							DisableNotifyInterrupt();
 | 
				
			||||||
		debug_query_string = NULL;
 | 
							debug_query_string = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2814,20 +2866,13 @@ PostgresMain(int argc, char *argv[], const char *username)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * (2) deal with pending asynchronous NOTIFY from other backends,
 | 
							 * (2) Allow asynchronous signals to be executed immediately
 | 
				
			||||||
		 * and enable async.c's signal handler to execute NOTIFY directly.
 | 
							 * if they come in while we are waiting for client input.
 | 
				
			||||||
		 * Then set up other stuff needed before blocking for input.
 | 
							 * (This must be conditional since we don't want, say, reads on
 | 
				
			||||||
 | 
							 * behalf of COPY FROM STDIN doing the same thing.)
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		QueryCancelPending = false;		/* forget any earlier CANCEL
 | 
							QueryCancelPending = false;		/* forget any earlier CANCEL signal */
 | 
				
			||||||
										 * signal */
 | 
							DoingCommandRead = true;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		EnableNotifyInterrupt();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Allow "die" interrupt to be processed while waiting */
 | 
					 | 
				
			||||||
		ImmediateInterruptOK = true;
 | 
					 | 
				
			||||||
		/* and don't forget to detect one that already arrived */
 | 
					 | 
				
			||||||
		QueryCancelPending = false;
 | 
					 | 
				
			||||||
		CHECK_FOR_INTERRUPTS();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * (3) read a command (loop blocks here)
 | 
							 * (3) read a command (loop blocks here)
 | 
				
			||||||
@@ -2837,10 +2882,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 | 
				
			|||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * (4) disable async signal conditions again.
 | 
							 * (4) disable async signal conditions again.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		ImmediateInterruptOK = false;
 | 
							DoingCommandRead = false;
 | 
				
			||||||
		QueryCancelPending = false;		/* forget any CANCEL signal */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		DisableNotifyInterrupt();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * (5) check for any other interesting events that happened while
 | 
							 * (5) check for any other interesting events that happened while
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
 | 
					 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
 | 
				
			||||||
 * Portions Copyright (c) 1994, Regents of the University of California
 | 
					 * Portions Copyright (c) 1994, Regents of the University of California
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * $Id: tcopprot.h,v 1.60 2003/08/04 02:40:15 momjian Exp $
 | 
					 * $Id: tcopprot.h,v 1.60.4.1 2005/06/02 21:04:08 tgl Exp $
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * OLD COMMENTS
 | 
					 * OLD COMMENTS
 | 
				
			||||||
 *	  This file was created so that other c files could get the two
 | 
					 *	  This file was created so that other c files could get the two
 | 
				
			||||||
@@ -49,6 +49,8 @@ extern List *pg_plan_queries(List *querytrees, bool needSnapshot);
 | 
				
			|||||||
extern void die(SIGNAL_ARGS);
 | 
					extern void die(SIGNAL_ARGS);
 | 
				
			||||||
extern void quickdie(SIGNAL_ARGS);
 | 
					extern void quickdie(SIGNAL_ARGS);
 | 
				
			||||||
extern void authdie(SIGNAL_ARGS);
 | 
					extern void authdie(SIGNAL_ARGS);
 | 
				
			||||||
 | 
					extern void prepare_for_client_read(void);
 | 
				
			||||||
 | 
					extern void client_read_ended(void);
 | 
				
			||||||
extern int	PostgresMain(int argc, char *argv[], const char *username);
 | 
					extern int	PostgresMain(int argc, char *argv[], const char *username);
 | 
				
			||||||
extern void ResetUsage(void);
 | 
					extern void ResetUsage(void);
 | 
				
			||||||
extern void ShowUsage(const char *title);
 | 
					extern void ShowUsage(const char *title);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user