Skip to content

Commit 12bbce1

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 f416622 commit 12bbce1

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
@@ -9,6 +9,7 @@
99
#include "funcapi.h"
1010
#include "libpq/pqformat.h"
1111
#include "utils/lsyscache.h"
12+
#include "utils/memutils.h"
1213
#include "utils/typcache.h"
1314

1415
#include "hstore.h"
@@ -437,6 +438,11 @@ hstore_recv(PG_FUNCTION_ARGS)
437438
PG_RETURN_POINTER(out);
438439
}
439440

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

442448
for (i = 0; i < pcount; ++i)
@@ -552,6 +558,13 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
552558
TEXTOID, -1, false, 'i',
553559
&key_datums, &key_nulls, &key_count);
554560

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

557570
if (PG_ARGISNULL(1))
@@ -674,6 +687,13 @@ hstore_from_array(PG_FUNCTION_ARGS)
674687

675688
count = in_count / 2;
676689

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

679699
for (i = 0; i < count; ++i)
@@ -805,6 +825,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
805825
my_extra->ncolumns = ncolumns;
806826
}
807827

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

810831
if (rec)

contrib/hstore/hstore_op.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "catalog/pg_type.h"
88
#include "funcapi.h"
99
#include "utils/builtins.h"
10+
#include "utils/memutils.h"
1011

1112
#include "hstore.h"
1213

@@ -89,6 +90,19 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
8990
return NULL;
9091
}
9192

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

94108
for (i = 0, j = 0; i < key_count; i++)
@@ -647,6 +661,7 @@ hstore_slice_to_hstore(PG_FUNCTION_ARGS)
647661
PG_RETURN_POINTER(out);
648662
}
649663

664+
/* hstoreArrayToPairs() checked overflow */
650665
out_pairs = palloc(sizeof(Pairs) * nkeys);
651666
bufsiz = 0;
652667

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
@@ -5,6 +5,7 @@
55

66
#include "fmgr.h"
77
#include "tsearch/ts_locale.h"
8+
#include "utils/memutils.h"
89

910
typedef struct
1011
{
@@ -111,6 +112,8 @@ typedef struct
111112

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

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
@@ -515,8 +515,13 @@ parse_tsquery(char *buf,
515515
return query;
516516
}
517517

518-
/* Pack the QueryItems in the final TSQuery struct to return to caller */
518+
if (TSQUERY_TOO_BIG(list_length(state.polstr), state.sumlen))
519+
ereport(ERROR,
520+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
521+
errmsg("tsquery is too large")));
519522
commonlen = COMPUTESIZE(list_length(state.polstr), state.sumlen);
523+
524+
/* Pack the QueryItems in the final TSQuery struct to return to caller */
520525
query = (TSQuery) palloc0(commonlen);
521526
SET_VARSIZE(query, commonlen);
522527
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
@@ -333,6 +333,11 @@ QTN2QT(QTNode *in)
333333
QTN2QTState state;
334334

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

338343
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
@@ -445,20 +448,12 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
445448
txid last = 0;
446449
int nxip;
447450
int i;
448-
int avail;
449-
int expect;
450451
txid xmin,
451452
xmax;
452453

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

464459
xmin = pq_getmsgint64(buf);

0 commit comments

Comments
 (0)