Skip to content

Commit 0b7026d

Browse files
committed
Predict integer overflow to avoid buffer overruns.
Several functions, mostly type input functions, calculated an allocation size such that the calculation wrapped to a small positive value when arguments implied a sufficiently-large requirement. Writes past the end of the inadvertent small allocation followed shortly thereafter. Coverity identified the path_in() vulnerability; code inspection led to the rest. In passing, add check_stack_depth() to prevent stack overflow in related functions. Back-patch to 8.4 (all supported versions). The non-comment hstore changes touch code that did not exist in 8.4, so that part stops at 9.0. Noah Misch and Heikki Linnakangas, reviewed by Tom Lane. Security: CVE-2014-0064
1 parent 6a10e57 commit 0b7026d

File tree

15 files changed

+169
-19
lines changed

15 files changed

+169
-19
lines changed

contrib/hstore/hstore.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,25 @@ typedef struct
4949
} HStore;
5050

5151
/*
52-
* it's not possible to get more than 2^28 items into an hstore,
53-
* so we reserve the top few bits of the size field. See hstore_compat.c
54-
* for one reason why. Some bits are left for future use here.
52+
* It's not possible to get more than 2^28 items into an hstore, so we reserve
53+
* the top few bits of the size field. See hstore_compat.c for one reason
54+
* why. Some bits are left for future use here. MaxAllocSize makes the
55+
* practical count limit slightly more than 2^28 / 3, or INT_MAX / 24, the
56+
* limit for an hstore full of 4-byte keys and null values. Therefore, we
57+
* don't explicitly check the format-imposed limit.
5558
*/
5659
#define HS_FLAG_NEWVERSION 0x80000000
5760

5861
#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF)
5962
#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION)
6063

6164

65+
/*
66+
* "x" comes from an existing HS_COUNT() (as discussed, <= INT_MAX/24) or a
67+
* Pairs array length (due to MaxAllocSize, <= INT_MAX/40). "lenstr" is no
68+
* more than INT_MAX, that extreme case arising in hstore_from_arrays().
69+
* Therefore, this calculation is limited to about INT_MAX / 5 + INT_MAX.
70+
*/
6271
#define HSHRDSIZE (sizeof(HStore))
6372
#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )
6473

contrib/hstore/hstore_io.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "funcapi.h"
1212
#include "libpq/pqformat.h"
1313
#include "utils/lsyscache.h"
14+
#include "utils/memutils.h"
1415
#include "utils/typcache.h"
1516

1617
#include "hstore.h"
@@ -438,6 +439,11 @@ hstore_recv(PG_FUNCTION_ARGS)
438439
PG_RETURN_POINTER(out);
439440
}
440441

442+
if (pcount < 0 || pcount > MaxAllocSize / sizeof(Pairs))
443+
ereport(ERROR,
444+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
445+
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
446+
pcount, (int) (MaxAllocSize / sizeof(Pairs)))));
441447
pairs = palloc(pcount * sizeof(Pairs));
442448

443449
for (i = 0; i < pcount; ++i)
@@ -553,6 +559,13 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
553559
TEXTOID, -1, false, 'i',
554560
&key_datums, &key_nulls, &key_count);
555561

562+
/* see discussion in hstoreArrayToPairs() */
563+
if (key_count > MaxAllocSize / sizeof(Pairs))
564+
ereport(ERROR,
565+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
566+
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
567+
key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
568+
556569
/* value_array might be NULL */
557570

558571
if (PG_ARGISNULL(1))
@@ -675,6 +688,13 @@ hstore_from_array(PG_FUNCTION_ARGS)
675688

676689
count = in_count / 2;
677690

691+
/* see discussion in hstoreArrayToPairs() */
692+
if (count > MaxAllocSize / sizeof(Pairs))
693+
ereport(ERROR,
694+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
695+
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
696+
count, (int) (MaxAllocSize / sizeof(Pairs)))));
697+
678698
pairs = palloc(count * sizeof(Pairs));
679699

680700
for (i = 0; i < count; ++i)
@@ -806,6 +826,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
806826
my_extra->ncolumns = ncolumns;
807827
}
808828

829+
Assert(ncolumns <= MaxTupleAttributeNumber); /* thus, no overflow */
809830
pairs = palloc(ncolumns * sizeof(Pairs));
810831

811832
if (rec)

contrib/hstore/hstore_op.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "catalog/pg_type.h"
1010
#include "funcapi.h"
1111
#include "utils/builtins.h"
12+
#include "utils/memutils.h"
1213

1314
#include "hstore.h"
1415

@@ -91,6 +92,19 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
9192
return NULL;
9293
}
9394

95+
/*
96+
* A text array uses at least eight bytes per element, so any overflow in
97+
* "key_count * sizeof(Pairs)" is small enough for palloc() to catch.
98+
* However, credible improvements to the array format could invalidate
99+
* that assumption. Therefore, use an explicit check rather than relying
100+
* on palloc() to complain.
101+
*/
102+
if (key_count > MaxAllocSize / sizeof(Pairs))
103+
ereport(ERROR,
104+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
105+
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
106+
key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
107+
94108
key_pairs = palloc(sizeof(Pairs) * key_count);
95109

96110
for (i = 0, j = 0; i < key_count; i++)
@@ -649,6 +663,7 @@ hstore_slice_to_hstore(PG_FUNCTION_ARGS)
649663
PG_RETURN_POINTER(out);
650664
}
651665

666+
/* hstoreArrayToPairs() checked overflow */
652667
out_pairs = palloc(sizeof(Pairs) * nkeys);
653668
bufsiz = 0;
654669

contrib/intarray/_int.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#define ___INT_H__
66

77
#include "utils/array.h"
8+
#include "utils/memutils.h"
89

910
/* number ranges for compression */
1011
#define MAXNUMRANGE 100
@@ -137,6 +138,7 @@ typedef struct QUERYTYPE
137138

138139
#define HDRSIZEQT offsetof(QUERYTYPE, items)
139140
#define COMPUTESIZE(size) ( HDRSIZEQT + (size) * sizeof(ITEM) )
141+
#define QUERYTYPEMAXITEMS ((MaxAllocSize - HDRSIZEQT) / sizeof(ITEM))
140142
#define GETQUERY(x) ( (x)->items )
141143

142144
/* "type" codes for ITEM */

contrib/intarray/_int_bool.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,9 @@ boolop(PG_FUNCTION_ARGS)
451451
static void
452452
findoprnd(ITEM *ptr, int4 *pos)
453453
{
454+
/* since this function recurses, it could be driven to stack overflow. */
455+
check_stack_depth();
456+
454457
#ifdef BS_DEBUG
455458
elog(DEBUG3, (ptr[*pos].type == OPR) ?
456459
"%d %c" : "%d %d", *pos, ptr[*pos].val);
@@ -511,7 +514,13 @@ bqarr_in(PG_FUNCTION_ARGS)
511514
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
512515
errmsg("empty query")));
513516

517+
if (state.num > QUERYTYPEMAXITEMS)
518+
ereport(ERROR,
519+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
520+
errmsg("number of query items (%d) exceeds the maximum allowed (%d)",
521+
state.num, (int) QUERYTYPEMAXITEMS)));
514522
commonlen = COMPUTESIZE(state.num);
523+
515524
query = (QUERYTYPE *) palloc(commonlen);
516525
SET_VARSIZE(query, commonlen);
517526
query->size = state.num;

contrib/ltree/ltree.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "postgres.h"
77
#include "fmgr.h"
88
#include "tsearch/ts_locale.h"
9+
#include "utils/memutils.h"
910

1011
typedef struct
1112
{
@@ -112,6 +113,8 @@ typedef struct
112113

113114
#define HDRSIZEQT MAXALIGN(VARHDRSZ + sizeof(int4))
114115
#define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + (size) * sizeof(ITEM) + (lenofoperand) )
116+
#define LTXTQUERY_TOO_BIG(size,lenofoperand) \
117+
((size) > (MaxAllocSize - HDRSIZEQT - (lenofoperand)) / sizeof(ITEM))
115118
#define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT )
116119
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) )
117120

contrib/ltree/ltree_io.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <ctype.h>
99

1010
#include "ltree.h"
11+
#include "utils/memutils.h"
1112
#include "crc32.h"
1213

1314
PG_FUNCTION_INFO_V1(ltree_in);
@@ -64,6 +65,11 @@ ltree_in(PG_FUNCTION_ARGS)
6465
ptr += charlen;
6566
}
6667

68+
if (num + 1 > MaxAllocSize / sizeof(nodeitem))
69+
ereport(ERROR,
70+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
71+
errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
72+
num + 1, (int) (MaxAllocSize / sizeof(nodeitem)))));
6773
list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
6874
ptr = buf;
6975
while (*ptr)
@@ -228,6 +234,11 @@ lquery_in(PG_FUNCTION_ARGS)
228234
}
229235

230236
num++;
237+
if (num > MaxAllocSize / ITEMSIZE)
238+
ereport(ERROR,
239+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
240+
errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
241+
num, (int) (MaxAllocSize / ITEMSIZE))));
231242
curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
232243
ptr = buf;
233244
while (*ptr)

contrib/ltree/ltxtquery_io.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "crc32.h"
1111
#include "ltree.h"
12+
#include "miscadmin.h"
1213

1314
PG_FUNCTION_INFO_V1(ltxtq_in);
1415
Datum ltxtq_in(PG_FUNCTION_ARGS);
@@ -213,6 +214,9 @@ makepol(QPRS_STATE *state)
213214
int4 lenstack = 0;
214215
uint16 flag = 0;
215216

217+
/* since this function recurses, it could be driven to stack overflow */
218+
check_stack_depth();
219+
216220
while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
217221
{
218222
switch (type)
@@ -277,6 +281,9 @@ makepol(QPRS_STATE *state)
277281
static void
278282
findoprnd(ITEM *ptr, int4 *pos)
279283
{
284+
/* since this function recurses, it could be driven to stack overflow. */
285+
check_stack_depth();
286+
280287
if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
281288
{
282289
ptr[*pos].left = 0;
@@ -341,8 +348,12 @@ queryin(char *buf)
341348
errmsg("syntax error"),
342349
errdetail("Empty query.")));
343350

344-
/* make finish struct */
351+
if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
352+
ereport(ERROR,
353+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
354+
errmsg("ltxtquery is too large")));
345355
commonlen = COMPUTESIZE(state.num, state.sumlen);
356+
346357
query = (ltxtquery *) palloc(commonlen);
347358
SET_VARSIZE(query, commonlen);
348359
query->size = state.num;

src/backend/utils/adt/geo_ops.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,7 @@ path_in(PG_FUNCTION_ARGS)
14031403
char *s;
14041404
int npts;
14051405
int size;
1406+
int base_size;
14061407
int depth = 0;
14071408

14081409
if ((npts = pair_count(str, ',')) <= 0)
@@ -1421,7 +1422,15 @@ path_in(PG_FUNCTION_ARGS)
14211422
depth++;
14221423
}
14231424

1424-
size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * npts;
1425+
base_size = sizeof(path->p[0]) * npts;
1426+
size = offsetof(PATH, p[0]) + base_size;
1427+
1428+
/* Check for integer overflow */
1429+
if (base_size / npts != sizeof(path->p[0]) || size <= base_size)
1430+
ereport(ERROR,
1431+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1432+
errmsg("too many points requested")));
1433+
14251434
path = (PATH *) palloc(size);
14261435

14271436
SET_VARSIZE(path, size);
@@ -3465,6 +3474,7 @@ poly_in(PG_FUNCTION_ARGS)
34653474
POLYGON *poly;
34663475
int npts;
34673476
int size;
3477+
int base_size;
34683478
int isopen;
34693479
char *s;
34703480

@@ -3473,7 +3483,15 @@ poly_in(PG_FUNCTION_ARGS)
34733483
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
34743484
errmsg("invalid input syntax for type polygon: \"%s\"", str)));
34753485

3476-
size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * npts;
3486+
base_size = sizeof(poly->p[0]) * npts;
3487+
size = offsetof(POLYGON, p[0]) + base_size;
3488+
3489+
/* Check for integer overflow */
3490+
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
3491+
ereport(ERROR,
3492+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3493+
errmsg("too many points requested")));
3494+
34773495
poly = (POLYGON *) palloc0(size); /* zero any holes */
34783496

34793497
SET_VARSIZE(poly, size);
@@ -4379,6 +4397,10 @@ path_poly(PG_FUNCTION_ARGS)
43794397
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
43804398
errmsg("open path cannot be converted to polygon")));
43814399

4400+
/*
4401+
* Never overflows: the old size fit in MaxAllocSize, and the new size is
4402+
* just a small constant larger.
4403+
*/
43824404
size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * path->npts;
43834405
poly = (POLYGON *) palloc(size);
43844406

@@ -4484,6 +4506,10 @@ poly_path(PG_FUNCTION_ARGS)
44844506
int size;
44854507
int i;
44864508

4509+
/*
4510+
* Never overflows: the old size fit in MaxAllocSize, and the new size is
4511+
* smaller by a small constant.
4512+
*/
44874513
size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * poly->npts;
44884514
path = (PATH *) palloc(size);
44894515

src/backend/utils/adt/tsquery.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,13 @@ parse_tsquery(char *buf,
517517
return query;
518518
}
519519

520-
/* Pack the QueryItems in the final TSQuery struct to return to caller */
520+
if (TSQUERY_TOO_BIG(list_length(state.polstr), state.sumlen))
521+
ereport(ERROR,
522+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
523+
errmsg("tsquery is too large")));
521524
commonlen = COMPUTESIZE(list_length(state.polstr), state.sumlen);
525+
526+
/* Pack the QueryItems in the final TSQuery struct to return to caller */
522527
query = (TSQuery) palloc0(commonlen);
523528
SET_VARSIZE(query, commonlen);
524529
query->size = list_length(state.polstr);

src/backend/utils/adt/tsquery_util.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,11 @@ QTN2QT(QTNode *in)
334334
QTN2QTState state;
335335

336336
cntsize(in, &sumlen, &nnode);
337+
338+
if (TSQUERY_TOO_BIG(nnode, sumlen))
339+
ereport(ERROR,
340+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
341+
errmsg("tsquery is too large")));
337342
len = COMPUTESIZE(nnode, sumlen);
338343

339344
out = (TSQuery) palloc0(len);

src/backend/utils/adt/txid.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "miscadmin.h"
2828
#include "libpq/pqformat.h"
2929
#include "utils/builtins.h"
30+
#include "utils/memutils.h"
3031
#include "utils/snapmgr.h"
3132

3233

@@ -66,6 +67,8 @@ typedef struct
6667

6768
#define TXID_SNAPSHOT_SIZE(nxip) \
6869
(offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
70+
#define TXID_SNAPSHOT_MAX_NXIP \
71+
((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
6972

7073
/*
7174
* Epoch values from xact.c
@@ -444,20 +447,12 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
444447
txid last = 0;
445448
int nxip;
446449
int i;
447-
int avail;
448-
int expect;
449450
txid xmin,
450451
xmax;
451452

452-
/*
453-
* load nxip and check for nonsense.
454-
*
455-
* (nxip > avail) check is against int overflows in 'expect'.
456-
*/
453+
/* load and validate nxip */
457454
nxip = pq_getmsgint(buf, 4);
458-
avail = buf->len - buf->cursor;
459-
expect = 8 + 8 + nxip * 8;
460-
if (nxip < 0 || nxip > avail || expect > avail)
455+
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
461456
goto bad_format;
462457

463458
xmin = pq_getmsgint64(buf);

0 commit comments

Comments
 (0)