Skip to content

Commit 09a8931

Browse files
committed
Repair bug that would allow libpq to think a command had succeeded when
it really hadn't, due to double output of previous command's response. Fix prevents recursive entry to libpq routines. Found by Jan Wieck.
1 parent 26b5d53 commit 09a8931

File tree

3 files changed

+93
-21
lines changed

3 files changed

+93
-21
lines changed

src/backend/libpq/pqcomm.c

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
3131
* Portions Copyright (c) 1994, Regents of the University of California
3232
*
33-
* $PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.171 2004/08/29 05:06:43 momjian Exp $
33+
* $PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.172 2004/09/26 00:26:19 tgl Exp $
3434
*
3535
*-------------------------------------------------------------------------
3636
*/
@@ -44,6 +44,7 @@
4444
* StreamClose - Close a client/backend connection
4545
* TouchSocketFile - Protect socket file against /tmp cleaners
4646
* pq_init - initialize libpq at backend startup
47+
* pq_comm_reset - reset libpq during error recovery
4748
* pq_close - shutdown libpq at backend exit
4849
*
4950
* low-level I/O:
@@ -88,21 +89,17 @@
8889
#include "storage/ipc.h"
8990

9091

91-
static void pq_close(int code, Datum arg);
92-
93-
#ifdef HAVE_UNIX_SOCKETS
94-
static int Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName);
95-
static int Setup_AF_UNIX(void);
96-
#endif /* HAVE_UNIX_SOCKETS */
97-
98-
9992
/*
10093
* Configuration options
10194
*/
10295
int Unix_socket_permissions;
10396
char *Unix_socket_group;
10497

10598

99+
/* Where the Unix socket file is */
100+
static char sock_path[MAXPGPATH];
101+
102+
106103
/*
107104
* Buffers for low-level I/O
108105
*/
@@ -121,9 +118,20 @@ static int PqRecvLength; /* End of data available in PqRecvBuffer */
121118
/*
122119
* Message status
123120
*/
121+
static bool PqCommBusy;
124122
static bool DoingCopyOut;
125123

126124

125+
/* Internal functions */
126+
static void pq_close(int code, Datum arg);
127+
static int internal_putbytes(const char *s, size_t len);
128+
static int internal_flush(void);
129+
#ifdef HAVE_UNIX_SOCKETS
130+
static int Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName);
131+
static int Setup_AF_UNIX(void);
132+
#endif /* HAVE_UNIX_SOCKETS */
133+
134+
127135
/* --------------------------------
128136
* pq_init - initialize libpq at backend startup
129137
* --------------------------------
@@ -132,10 +140,27 @@ void
132140
pq_init(void)
133141
{
134142
PqSendPointer = PqRecvPointer = PqRecvLength = 0;
143+
PqCommBusy = false;
135144
DoingCopyOut = false;
136145
on_proc_exit(pq_close, 0);
137146
}
138147

148+
/* --------------------------------
149+
* pq_comm_reset - reset libpq during error recovery
150+
*
151+
* This is called from error recovery at the outer idle loop. It's
152+
* just to get us out of trouble if we somehow manage to elog() from
153+
* inside a pqcomm.c routine (which ideally will never happen, but...)
154+
* --------------------------------
155+
*/
156+
void
157+
pq_comm_reset(void)
158+
{
159+
/* Do not throw away pending data, but do reset the busy flag */
160+
PqCommBusy = false;
161+
/* We can abort any old-style COPY OUT, too */
162+
pq_endcopyout(true);
163+
}
139164

140165
/* --------------------------------
141166
* pq_close - shutdown libpq at backend exit
@@ -174,8 +199,6 @@ pq_close(int code, Datum arg)
174199
* Stream functions are used for vanilla TCP connection protocol.
175200
*/
176201

177-
static char sock_path[MAXPGPATH];
178-
179202

180203
/* StreamDoUnlink()
181204
* Shutdown routine for backend connection
@@ -885,13 +908,30 @@ pq_getmessage(StringInfo s, int maxlen)
885908
*/
886909
int
887910
pq_putbytes(const char *s, size_t len)
911+
{
912+
int res;
913+
914+
/* Should only be called by old-style COPY OUT */
915+
Assert(DoingCopyOut);
916+
/* No-op if reentrant call */
917+
if (PqCommBusy)
918+
return 0;
919+
PqCommBusy = true;
920+
res = internal_putbytes(s, len);
921+
PqCommBusy = false;
922+
return res;
923+
}
924+
925+
static int
926+
internal_putbytes(const char *s, size_t len)
888927
{
889928
size_t amount;
890929

891930
while (len > 0)
892931
{
932+
/* If buffer is full, then flush it out */
893933
if (PqSendPointer >= PQ_BUFFER_SIZE)
894-
if (pq_flush()) /* If buffer is full, then flush it out */
934+
if (internal_flush())
895935
return EOF;
896936
amount = PQ_BUFFER_SIZE - PqSendPointer;
897937
if (amount > len)
@@ -912,6 +952,20 @@ pq_putbytes(const char *s, size_t len)
912952
*/
913953
int
914954
pq_flush(void)
955+
{
956+
int res;
957+
958+
/* No-op if reentrant call */
959+
if (PqCommBusy)
960+
return 0;
961+
PqCommBusy = true;
962+
res = internal_flush();
963+
PqCommBusy = false;
964+
return res;
965+
}
966+
967+
static int
968+
internal_flush(void)
915969
{
916970
static int last_reported_send_errno = 0;
917971

@@ -988,26 +1042,40 @@ pq_flush(void)
9881042
* then; dropping them is annoying, but at least they will still appear
9891043
* in the postmaster log.)
9901044
*
1045+
* We also suppress messages generated while pqcomm.c is busy. This
1046+
* avoids any possibility of messages being inserted within other
1047+
* messages. The only known trouble case arises if SIGQUIT occurs
1048+
* during a pqcomm.c routine --- quickdie() will try to send a warning
1049+
* message, and the most reasonable approach seems to be to drop it.
1050+
*
9911051
* returns 0 if OK, EOF if trouble
9921052
* --------------------------------
9931053
*/
9941054
int
9951055
pq_putmessage(char msgtype, const char *s, size_t len)
9961056
{
997-
if (DoingCopyOut)
1057+
if (DoingCopyOut || PqCommBusy)
9981058
return 0;
1059+
PqCommBusy = true;
9991060
if (msgtype)
1000-
if (pq_putbytes(&msgtype, 1))
1001-
return EOF;
1061+
if (internal_putbytes(&msgtype, 1))
1062+
goto fail;
10021063
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
10031064
{
10041065
uint32 n32;
10051066

10061067
n32 = htonl((uint32) (len + 4));
1007-
if (pq_putbytes((char *) &n32, 4))
1008-
return EOF;
1068+
if (internal_putbytes((char *) &n32, 4))
1069+
goto fail;
10091070
}
1010-
return pq_putbytes(s, len);
1071+
if (internal_putbytes(s, len))
1072+
goto fail;
1073+
PqCommBusy = false;
1074+
return 0;
1075+
1076+
fail:
1077+
PqCommBusy = false;
1078+
return EOF;
10111079
}
10121080

10131081
/* --------------------------------
@@ -1036,8 +1104,8 @@ pq_endcopyout(bool errorAbort)
10361104
{
10371105
if (!DoingCopyOut)
10381106
return;
1039-
DoingCopyOut = false;
10401107
if (errorAbort)
10411108
pq_putbytes("\n\n\\.\n", 5);
10421109
/* in non-error case, copy.c will have emitted the terminator line */
1110+
DoingCopyOut = false;
10431111
}

src/backend/tcop/postgres.c

Lines changed: 4 additions & 1 deletion
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.432 2004/09/13 20:07:05 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.433 2004/09/26 00:26:25 tgl Exp $
1212
*
1313
* NOTES
1414
* this is the "main" module of the postgres backend and
@@ -2811,6 +2811,9 @@ PostgresMain(int argc, char *argv[], const char *username)
28112811
DisableNotifyInterrupt();
28122812
DisableCatchupInterrupt();
28132813

2814+
/* Make sure libpq is in a good state */
2815+
pq_comm_reset();
2816+
28142817
/* Report the error to the client and/or server log */
28152818
EmitErrorReport();
28162819

src/include/libpq/libpq.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/libpq/libpq.h,v 1.62 2004/08/29 04:13:07 momjian Exp $
10+
* $PostgreSQL: pgsql/src/include/libpq/libpq.h,v 1.63 2004/09/26 00:26:28 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -52,6 +52,7 @@ extern int StreamConnection(int server_fd, Port *port);
5252
extern void StreamClose(int sock);
5353
extern void TouchSocketFile(void);
5454
extern void pq_init(void);
55+
extern void pq_comm_reset(void);
5556
extern int pq_getbytes(char *s, size_t len);
5657
extern int pq_getstring(StringInfo s);
5758
extern int pq_getmessage(StringInfo s, int maxlen);

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