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

Commit 739259d

Browse files
committed
Adjust the behavior of the PQExpBuffer code to make it have well-defined
results (ie, an empty "broken" buffer) if memory overrun occurs anywhere along the way to filling the buffer. The previous coding would just silently discard portions of the intended buffer contents, as exhibited in trouble report from Sam Mason. Also, tweak psql's main loop to correctly detect and report such overruns. There's probably much more that should be done in this line, but this is a start.
1 parent 8a10096 commit 739259d

File tree

6 files changed

+124
-27
lines changed

6 files changed

+124
-27
lines changed

src/bin/psql/input.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2008, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.64 2008/01/01 19:45:56 momjian Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.65 2008/11/26 00:26:23 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99

@@ -184,14 +184,21 @@ gets_fromFile(FILE *source)
184184
{
185185
if (ferror(source))
186186
{
187-
psql_error("could not read from input file: %s\n", strerror(errno));
187+
psql_error("could not read from input file: %s\n",
188+
strerror(errno));
188189
return NULL;
189190
}
190191
break;
191192
}
192193

193194
appendPQExpBufferStr(buffer, line);
194195

196+
if (PQExpBufferBroken(buffer))
197+
{
198+
psql_error("out of memory\n");
199+
return NULL;
200+
}
201+
195202
/* EOL? */
196203
if (buffer->data[buffer->len - 1] == '\n')
197204
{

src/bin/psql/mainloop.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2008, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.92 2008/06/10 20:58:19 neilc Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.93 2008/11/26 00:26:23 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99
#include "mainloop.h"
@@ -26,9 +26,9 @@ int
2626
MainLoop(FILE *source)
2727
{
2828
PsqlScanState scan_state; /* lexer working state */
29-
PQExpBuffer query_buf; /* buffer for query being accumulated */
30-
PQExpBuffer previous_buf; /* if there isn't anything in the new buffer
31-
* yet, use this one for \e, etc. */
29+
volatile PQExpBuffer query_buf; /* buffer for query being accumulated */
30+
volatile PQExpBuffer previous_buf; /* if there isn't anything in the new
31+
* buffer yet, use this one for \e, etc. */
3232
PQExpBuffer history_buf; /* earlier lines of a multi-line command, not
3333
* yet saved to readline history */
3434
char *line; /* current line of input */
@@ -62,7 +62,9 @@ MainLoop(FILE *source)
6262
query_buf = createPQExpBuffer();
6363
previous_buf = createPQExpBuffer();
6464
history_buf = createPQExpBuffer();
65-
if (!query_buf || !previous_buf || !history_buf)
65+
if (PQExpBufferBroken(query_buf) ||
66+
PQExpBufferBroken(previous_buf) ||
67+
PQExpBufferBroken(history_buf))
6668
{
6769
psql_error("out of memory\n");
6870
exit(EXIT_FAILURE);
@@ -220,6 +222,12 @@ MainLoop(FILE *source)
220222
scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
221223
prompt_status = prompt_tmp;
222224

225+
if (PQExpBufferBroken(query_buf))
226+
{
227+
psql_error("out of memory\n");
228+
exit(EXIT_FAILURE);
229+
}
230+
223231
/*
224232
* Send command if semicolon found, or if end of line and we're in
225233
* single-line mode.
@@ -242,9 +250,15 @@ MainLoop(FILE *source)
242250
success = SendQuery(query_buf->data);
243251
slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
244252

245-
resetPQExpBuffer(previous_buf);
246-
appendPQExpBufferStr(previous_buf, query_buf->data);
253+
/* transfer query to previous_buf by pointer-swapping */
254+
{
255+
PQExpBuffer swap_buf = previous_buf;
256+
257+
previous_buf = query_buf;
258+
query_buf = swap_buf;
259+
}
247260
resetPQExpBuffer(query_buf);
261+
248262
added_nl_pos = -1;
249263
/* we need not do psql_scan_reset() here */
250264
}
@@ -294,8 +308,13 @@ MainLoop(FILE *source)
294308
{
295309
success = SendQuery(query_buf->data);
296310

297-
resetPQExpBuffer(previous_buf);
298-
appendPQExpBufferStr(previous_buf, query_buf->data);
311+
/* transfer query to previous_buf by pointer-swapping */
312+
{
313+
PQExpBuffer swap_buf = previous_buf;
314+
315+
previous_buf = query_buf;
316+
query_buf = swap_buf;
317+
}
299318
resetPQExpBuffer(query_buf);
300319

301320
/* flush any paren nesting info after forced send */

src/bin/psql/psqlscan.l

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
* Portions Copyright (c) 1994, Regents of the University of California
3434
*
3535
* IDENTIFICATION
36-
* $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.26 2008/10/29 08:04:53 petere Exp $
36+
* $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.27 2008/11/26 00:26:23 tgl Exp $
3737
*
3838
*-------------------------------------------------------------------------
3939
*/
@@ -1453,6 +1453,12 @@ psql_scan_slash_option(PsqlScanState state,
14531453
error = true;
14541454
}
14551455

1456+
if (PQExpBufferBroken(&output))
1457+
{
1458+
psql_error("%s: out of memory\n", cmd);
1459+
error = true;
1460+
}
1461+
14561462
/* Now done with cmd, transfer result to mybuf */
14571463
resetPQExpBuffer(&mybuf);
14581464

src/interfaces/libpq/fe-connect.c

Lines changed: 5 additions & 5 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.369 2008/11/25 19:30:42 tgl Exp $
11+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.370 2008/11/26 00:26:23 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -585,7 +585,7 @@ PQconndefaults(void)
585585
PQconninfoOption *connOptions;
586586

587587
initPQExpBuffer(&errorBuf);
588-
if (errorBuf.data == NULL)
588+
if (PQExpBufferBroken(&errorBuf))
589589
return NULL; /* out of memory already :-( */
590590
connOptions = conninfo_parse("", &errorBuf, true);
591591
termPQExpBuffer(&errorBuf);
@@ -1970,8 +1970,8 @@ makeEmptyPGconn(void)
19701970

19711971
if (conn->inBuffer == NULL ||
19721972
conn->outBuffer == NULL ||
1973-
conn->errorMessage.data == NULL ||
1974-
conn->workBuffer.data == NULL)
1973+
PQExpBufferBroken(&conn->errorMessage) ||
1974+
PQExpBufferBroken(&conn->workBuffer))
19751975
{
19761976
/* out of memory already :-( */
19771977
freePGconn(conn);
@@ -3151,7 +3151,7 @@ PQconninfoParse(const char *conninfo, char **errmsg)
31513151
if (errmsg)
31523152
*errmsg = NULL; /* default */
31533153
initPQExpBuffer(&errorBuf);
3154-
if (errorBuf.data == NULL)
3154+
if (PQExpBufferBroken(&errorBuf))
31553155
return NULL; /* out of memory already :-( */
31563156
connOptions = conninfo_parse(conninfo, &errorBuf, false);
31573157
if (connOptions == NULL && errmsg)

src/interfaces/libpq/pqexpbuffer.c

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
1818
* Portions Copyright (c) 1994, Regents of the University of California
1919
*
20-
* $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.24 2008/01/01 19:46:00 momjian Exp $
20+
* $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.25 2008/11/26 00:26:23 tgl Exp $
2121
*
2222
*-------------------------------------------------------------------------
2323
*/
@@ -32,6 +32,32 @@
3232
#include "win32.h"
3333
#endif
3434

35+
36+
/* All "broken" PQExpBuffers point to this string. */
37+
static const char oom_buffer[1] = "";
38+
39+
40+
/*
41+
* markPQExpBufferBroken
42+
*
43+
* Put a PQExpBuffer in "broken" state if it isn't already.
44+
*/
45+
static void
46+
markPQExpBufferBroken(PQExpBuffer str)
47+
{
48+
if (str->data != oom_buffer)
49+
free(str->data);
50+
/*
51+
* Casting away const here is a bit ugly, but it seems preferable to
52+
* not marking oom_buffer const. We want to do that to encourage the
53+
* compiler to put oom_buffer in read-only storage, so that anyone who
54+
* tries to scribble on a broken PQExpBuffer will get a failure.
55+
*/
56+
str->data = (char *) oom_buffer;
57+
str->len = 0;
58+
str->maxlen = 0;
59+
}
60+
3561
/*
3662
* createPQExpBuffer
3763
*
@@ -61,6 +87,7 @@ initPQExpBuffer(PQExpBuffer str)
6187
str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
6288
if (str->data == NULL)
6389
{
90+
str->data = (char *) oom_buffer; /* see comment above */
6491
str->maxlen = 0;
6592
str->len = 0;
6693
}
@@ -96,28 +123,35 @@ destroyPQExpBuffer(PQExpBuffer str)
96123
void
97124
termPQExpBuffer(PQExpBuffer str)
98125
{
99-
if (str->data)
100-
{
126+
if (str->data != oom_buffer)
101127
free(str->data);
102-
str->data = NULL;
103-
}
104128
/* just for luck, make the buffer validly empty. */
129+
str->data = (char *) oom_buffer; /* see comment above */
105130
str->maxlen = 0;
106131
str->len = 0;
107132
}
108133

109134
/*
110135
* resetPQExpBuffer
111136
* Reset a PQExpBuffer to empty
137+
*
138+
* Note: if possible, a "broken" PQExpBuffer is returned to normal.
112139
*/
113140
void
114141
resetPQExpBuffer(PQExpBuffer str)
115142
{
116143
if (str)
117144
{
118-
str->len = 0;
119-
if (str->data)
145+
if (str->data != oom_buffer)
146+
{
147+
str->len = 0;
120148
str->data[0] = '\0';
149+
}
150+
else
151+
{
152+
/* try to reinitialize to valid state */
153+
initPQExpBuffer(str);
154+
}
121155
}
122156
}
123157

@@ -126,21 +160,28 @@ resetPQExpBuffer(PQExpBuffer str)
126160
* Make sure there is enough space for 'needed' more bytes in the buffer
127161
* ('needed' does not include the terminating null).
128162
*
129-
* Returns 1 if OK, 0 if failed to enlarge buffer.
163+
* Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
164+
* the buffer is left in "broken" state.)
130165
*/
131166
int
132167
enlargePQExpBuffer(PQExpBuffer str, size_t needed)
133168
{
134169
size_t newlen;
135170
char *newdata;
136171

172+
if (PQExpBufferBroken(str))
173+
return 0; /* already failed */
174+
137175
/*
138176
* Guard against ridiculous "needed" values, which can occur if we're fed
139177
* bogus data. Without this, we can get an overflow or infinite loop in
140178
* the following.
141179
*/
142180
if (needed >= ((size_t) INT_MAX - str->len))
181+
{
182+
markPQExpBufferBroken(str);
143183
return 0;
184+
}
144185

145186
needed += str->len + 1; /* total space required now */
146187

@@ -173,6 +214,8 @@ enlargePQExpBuffer(PQExpBuffer str, size_t needed)
173214
str->maxlen = newlen;
174215
return 1;
175216
}
217+
218+
markPQExpBufferBroken(str);
176219
return 0;
177220
}
178221

@@ -192,6 +235,9 @@ printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
192235

193236
resetPQExpBuffer(str);
194237

238+
if (PQExpBufferBroken(str))
239+
return; /* already failed */
240+
195241
for (;;)
196242
{
197243
/*
@@ -240,6 +286,9 @@ appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
240286
size_t avail;
241287
int nprinted;
242288

289+
if (PQExpBufferBroken(str))
290+
return; /* already failed */
291+
243292
for (;;)
244293
{
245294
/*

src/interfaces/libpq/pqexpbuffer.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
1919
* Portions Copyright (c) 1994, Regents of the University of California
2020
*
21-
* $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.h,v 1.19 2008/01/01 19:46:00 momjian Exp $
21+
* $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.h,v 1.20 2008/11/26 00:26:23 tgl Exp $
2222
*
2323
*-------------------------------------------------------------------------
2424
*/
@@ -35,6 +35,10 @@
3535
* string size (including the terminating '\0' char) that we can
3636
* currently store in 'data' without having to reallocate
3737
* more space. We must always have maxlen > len.
38+
*
39+
* An exception occurs if we failed to allocate enough memory for the string
40+
* buffer. In that case data points to a statically allocated empty string,
41+
* and len = maxlen = 0.
3842
*-------------------------
3943
*/
4044
typedef struct PQExpBufferData
@@ -46,6 +50,15 @@ typedef struct PQExpBufferData
4650

4751
typedef PQExpBufferData *PQExpBuffer;
4852

53+
/*------------------------
54+
* Test for a broken (out of memory) PQExpBuffer.
55+
* When a buffer is "broken", all operations except resetting or deleting it
56+
* are no-ops.
57+
*------------------------
58+
*/
59+
#define PQExpBufferBroken(str) \
60+
(!(str) || (str)->maxlen == 0)
61+
4962
/*------------------------
5063
* Initial size of the data buffer in a PQExpBuffer.
5164
* NB: this must be large enough to hold error messages that might
@@ -103,6 +116,8 @@ extern void termPQExpBuffer(PQExpBuffer str);
103116
/*------------------------
104117
* resetPQExpBuffer
105118
* Reset a PQExpBuffer to empty
119+
*
120+
* Note: if possible, a "broken" PQExpBuffer is returned to normal.
106121
*/
107122
extern void resetPQExpBuffer(PQExpBuffer str);
108123

@@ -111,7 +126,8 @@ extern void resetPQExpBuffer(PQExpBuffer str);
111126
* Make sure there is enough space for 'needed' more bytes in the buffer
112127
* ('needed' does not include the terminating null).
113128
*
114-
* Returns 1 if OK, 0 if failed to enlarge buffer.
129+
* Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
130+
* the buffer is left in "broken" state.)
115131
*/
116132
extern int enlargePQExpBuffer(PQExpBuffer str, size_t needed);
117133

0 commit comments

Comments
 (0)