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

Commit 7a362a1

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 e4a4fa2 commit 7a362a1

File tree

15 files changed

+177
-19
lines changed

15 files changed

+177
-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
@@ -13,6 +13,7 @@
1313
#include "utils/builtins.h"
1414
#include "utils/json.h"
1515
#include "utils/lsyscache.h"
16+
#include "utils/memutils.h"
1617
#include "utils/typcache.h"
1718

1819
#include "hstore.h"
@@ -439,6 +440,11 @@ hstore_recv(PG_FUNCTION_ARGS)
439440
PG_RETURN_POINTER(out);
440441
}
441442

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

444450
for (i = 0; i < pcount; ++i)
@@ -554,6 +560,13 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
554560
TEXTOID, -1, false, 'i',
555561
&key_datums, &key_nulls, &key_count);
556562

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

559572
if (PG_ARGISNULL(1))
@@ -676,6 +689,13 @@ hstore_from_array(PG_FUNCTION_ARGS)
676689

677690
count = in_count / 2;
678691

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

681701
for (i = 0; i < count; ++i)
@@ -807,6 +827,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
807827
my_extra->ncolumns = ncolumns;
808828
}
809829

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

812833
if (rec)

contrib/hstore/hstore_op.c

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

1213
#include "hstore.h"
1314

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

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

95109
for (i = 0, j = 0; i < key_count; i++)
@@ -648,6 +662,7 @@ hstore_slice_to_hstore(PG_FUNCTION_ARGS)
648662
PG_RETURN_POINTER(out);
649663
}
650664

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

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
@@ -448,6 +448,9 @@ boolop(PG_FUNCTION_ARGS)
448448
static void
449449
findoprnd(ITEM *ptr, int32 *pos)
450450
{
451+
/* since this function recurses, it could be driven to stack overflow. */
452+
check_stack_depth();
453+
451454
#ifdef BS_DEBUG
452455
elog(DEBUG3, (ptr[*pos].type == OPR) ?
453456
"%d %c" : "%d %d", *pos, ptr[*pos].val);
@@ -508,7 +511,13 @@ bqarr_in(PG_FUNCTION_ARGS)
508511
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
509512
errmsg("empty query")));
510513

514+
if (state.num > QUERYTYPEMAXITEMS)
515+
ereport(ERROR,
516+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
517+
errmsg("number of query items (%d) exceeds the maximum allowed (%d)",
518+
state.num, (int) QUERYTYPEMAXITEMS)));
511519
commonlen = COMPUTESIZE(state.num);
520+
512521
query = (QUERYTYPE *) palloc(commonlen);
513522
SET_VARSIZE(query, commonlen);
514523
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(int32))
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);
@@ -212,6 +213,9 @@ makepol(QPRS_STATE *state)
212213
int32 lenstack = 0;
213214
uint16 flag = 0;
214215

216+
/* since this function recurses, it could be driven to stack overflow */
217+
check_stack_depth();
218+
215219
while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
216220
{
217221
switch (type)
@@ -276,6 +280,9 @@ makepol(QPRS_STATE *state)
276280
static void
277281
findoprnd(ITEM *ptr, int32 *pos)
278282
{
283+
/* since this function recurses, it could be driven to stack overflow. */
284+
check_stack_depth();
285+
279286
if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
280287
{
281288
ptr[*pos].left = 0;
@@ -340,8 +347,12 @@ queryin(char *buf)
340347
errmsg("syntax error"),
341348
errdetail("Empty query.")));
342349

343-
/* make finish struct */
350+
if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
351+
ereport(ERROR,
352+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
353+
errmsg("ltxtquery is too large")));
344354
commonlen = COMPUTESIZE(state.num, state.sumlen);
355+
345356
query = (ltxtquery *) palloc(commonlen);
346357
SET_VARSIZE(query, commonlen);
347358
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
@@ -1398,6 +1398,7 @@ path_in(PG_FUNCTION_ARGS)
13981398
char *s;
13991399
int npts;
14001400
int size;
1401+
int base_size;
14011402
int depth = 0;
14021403

14031404
if ((npts = pair_count(str, ',')) <= 0)
@@ -1416,7 +1417,15 @@ path_in(PG_FUNCTION_ARGS)
14161417
depth++;
14171418
}
14181419

1419-
size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * npts;
1420+
base_size = sizeof(path->p[0]) * npts;
1421+
size = offsetof(PATH, p[0]) + base_size;
1422+
1423+
/* Check for integer overflow */
1424+
if (base_size / npts != sizeof(path->p[0]) || size <= base_size)
1425+
ereport(ERROR,
1426+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1427+
errmsg("too many points requested")));
1428+
14201429
path = (PATH *) palloc(size);
14211430

14221431
SET_VARSIZE(path, size);
@@ -3460,6 +3469,7 @@ poly_in(PG_FUNCTION_ARGS)
34603469
POLYGON *poly;
34613470
int npts;
34623471
int size;
3472+
int base_size;
34633473
int isopen;
34643474
char *s;
34653475

@@ -3468,7 +3478,15 @@ poly_in(PG_FUNCTION_ARGS)
34683478
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
34693479
errmsg("invalid input syntax for type polygon: \"%s\"", str)));
34703480

3471-
size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * npts;
3481+
base_size = sizeof(poly->p[0]) * npts;
3482+
size = offsetof(POLYGON, p[0]) + base_size;
3483+
3484+
/* Check for integer overflow */
3485+
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
3486+
ereport(ERROR,
3487+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3488+
errmsg("too many points requested")));
3489+
34723490
poly = (POLYGON *) palloc0(size); /* zero any holes */
34733491

34743492
SET_VARSIZE(poly, size);
@@ -4374,6 +4392,10 @@ path_poly(PG_FUNCTION_ARGS)
43744392
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
43754393
errmsg("open path cannot be converted to polygon")));
43764394

4395+
/*
4396+
* Never overflows: the old size fit in MaxAllocSize, and the new size is
4397+
* just a small constant larger.
4398+
*/
43774399
size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * path->npts;
43784400
poly = (POLYGON *) palloc(size);
43794401

@@ -4479,6 +4501,10 @@ poly_path(PG_FUNCTION_ARGS)
44794501
int size;
44804502
int i;
44814503

4504+
/*
4505+
* Never overflows: the old size fit in MaxAllocSize, and the new size is
4506+
* smaller by a small constant.
4507+
*/
44824508
size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * poly->npts;
44834509
path = (PATH *) palloc(size);
44844510

src/backend/utils/adt/tsquery.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,8 +514,13 @@ parse_tsquery(char *buf,
514514
return query;
515515
}
516516

517-
/* Pack the QueryItems in the final TSQuery struct to return to caller */
517+
if (TSQUERY_TOO_BIG(list_length(state.polstr), state.sumlen))
518+
ereport(ERROR,
519+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
520+
errmsg("tsquery is too large")));
518521
commonlen = COMPUTESIZE(list_length(state.polstr), state.sumlen);
522+
523+
/* Pack the QueryItems in the final TSQuery struct to return to caller */
519524
query = (TSQuery) palloc0(commonlen);
520525
SET_VARSIZE(query, commonlen);
521526
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);

0 commit comments

Comments
 (0)