Skip to content

Commit

Permalink
Predict integer overflow to avoid buffer overruns.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nmisch committed Feb 17, 2014
1 parent 4318dae commit 31400a6
Show file tree
Hide file tree
Showing 15 changed files with 177 additions and 19 deletions.
15 changes: 12 additions & 3 deletions contrib/hstore/hstore.h
Expand Up @@ -49,16 +49,25 @@ typedef struct
} HStore;

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

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


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

Expand Down
21 changes: 21 additions & 0 deletions contrib/hstore/hstore_io.c
Expand Up @@ -13,6 +13,7 @@
#include "utils/builtins.h"
#include "utils/json.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"

#include "hstore.h"
Expand Down Expand Up @@ -439,6 +440,11 @@ hstore_recv(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(out);
}

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

for (i = 0; i < pcount; ++i)
Expand Down Expand Up @@ -554,6 +560,13 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
TEXTOID, -1, false, 'i',
&key_datums, &key_nulls, &key_count);

/* see discussion in hstoreArrayToPairs() */
if (key_count > MaxAllocSize / sizeof(Pairs))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
key_count, (int) (MaxAllocSize / sizeof(Pairs)))));

/* value_array might be NULL */

if (PG_ARGISNULL(1))
Expand Down Expand Up @@ -676,6 +689,13 @@ hstore_from_array(PG_FUNCTION_ARGS)

count = in_count / 2;

/* see discussion in hstoreArrayToPairs() */
if (count > MaxAllocSize / sizeof(Pairs))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
count, (int) (MaxAllocSize / sizeof(Pairs)))));

pairs = palloc(count * sizeof(Pairs));

for (i = 0; i < count; ++i)
Expand Down Expand Up @@ -807,6 +827,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
my_extra->ncolumns = ncolumns;
}

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

if (rec)
Expand Down
15 changes: 15 additions & 0 deletions contrib/hstore/hstore_op.c
Expand Up @@ -8,6 +8,7 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "utils/builtins.h"
#include "utils/memutils.h"

#include "hstore.h"

Expand Down Expand Up @@ -90,6 +91,19 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
return NULL;
}

/*
* A text array uses at least eight bytes per element, so any overflow in
* "key_count * sizeof(Pairs)" is small enough for palloc() to catch.
* However, credible improvements to the array format could invalidate
* that assumption. Therefore, use an explicit check rather than relying
* on palloc() to complain.
*/
if (key_count > MaxAllocSize / sizeof(Pairs))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
key_count, (int) (MaxAllocSize / sizeof(Pairs)))));

key_pairs = palloc(sizeof(Pairs) * key_count);

for (i = 0, j = 0; i < key_count; i++)
Expand Down Expand Up @@ -648,6 +662,7 @@ hstore_slice_to_hstore(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(out);
}

/* hstoreArrayToPairs() checked overflow */
out_pairs = palloc(sizeof(Pairs) * nkeys);
bufsiz = 0;

Expand Down
2 changes: 2 additions & 0 deletions contrib/intarray/_int.h
Expand Up @@ -5,6 +5,7 @@
#define ___INT_H__

#include "utils/array.h"
#include "utils/memutils.h"

/* number ranges for compression */
#define MAXNUMRANGE 100
Expand Down Expand Up @@ -137,6 +138,7 @@ typedef struct QUERYTYPE

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

/* "type" codes for ITEM */
Expand Down
9 changes: 9 additions & 0 deletions contrib/intarray/_int_bool.c
Expand Up @@ -448,6 +448,9 @@ boolop(PG_FUNCTION_ARGS)
static void
findoprnd(ITEM *ptr, int32 *pos)
{
/* since this function recurses, it could be driven to stack overflow. */
check_stack_depth();

#ifdef BS_DEBUG
elog(DEBUG3, (ptr[*pos].type == OPR) ?
"%d %c" : "%d %d", *pos, ptr[*pos].val);
Expand Down Expand Up @@ -508,7 +511,13 @@ bqarr_in(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("empty query")));

if (state.num > QUERYTYPEMAXITEMS)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of query items (%d) exceeds the maximum allowed (%d)",
state.num, (int) QUERYTYPEMAXITEMS)));
commonlen = COMPUTESIZE(state.num);

query = (QUERYTYPE *) palloc(commonlen);
SET_VARSIZE(query, commonlen);
query->size = state.num;
Expand Down
3 changes: 3 additions & 0 deletions contrib/ltree/ltree.h
Expand Up @@ -5,6 +5,7 @@

#include "fmgr.h"
#include "tsearch/ts_locale.h"
#include "utils/memutils.h"

typedef struct
{
Expand Down Expand Up @@ -111,6 +112,8 @@ typedef struct

#define HDRSIZEQT MAXALIGN(VARHDRSZ + sizeof(int32))
#define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + (size) * sizeof(ITEM) + (lenofoperand) )
#define LTXTQUERY_TOO_BIG(size,lenofoperand) \
((size) > (MaxAllocSize - HDRSIZEQT - (lenofoperand)) / sizeof(ITEM))
#define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT )
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) )

Expand Down
11 changes: 11 additions & 0 deletions contrib/ltree/ltree_io.c
Expand Up @@ -8,6 +8,7 @@
#include <ctype.h>

#include "ltree.h"
#include "utils/memutils.h"
#include "crc32.h"

PG_FUNCTION_INFO_V1(ltree_in);
Expand Down Expand Up @@ -64,6 +65,11 @@ ltree_in(PG_FUNCTION_ARGS)
ptr += charlen;
}

if (num + 1 > MaxAllocSize / sizeof(nodeitem))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
num + 1, (int) (MaxAllocSize / sizeof(nodeitem)))));
list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
ptr = buf;
while (*ptr)
Expand Down Expand Up @@ -228,6 +234,11 @@ lquery_in(PG_FUNCTION_ARGS)
}

num++;
if (num > MaxAllocSize / ITEMSIZE)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
num, (int) (MaxAllocSize / ITEMSIZE))));
curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
ptr = buf;
while (*ptr)
Expand Down
13 changes: 12 additions & 1 deletion contrib/ltree/ltxtquery_io.c
Expand Up @@ -9,6 +9,7 @@

#include "crc32.h"
#include "ltree.h"
#include "miscadmin.h"

PG_FUNCTION_INFO_V1(ltxtq_in);
Datum ltxtq_in(PG_FUNCTION_ARGS);
Expand Down Expand Up @@ -212,6 +213,9 @@ makepol(QPRS_STATE *state)
int32 lenstack = 0;
uint16 flag = 0;

/* since this function recurses, it could be driven to stack overflow */
check_stack_depth();

while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
{
switch (type)
Expand Down Expand Up @@ -276,6 +280,9 @@ makepol(QPRS_STATE *state)
static void
findoprnd(ITEM *ptr, int32 *pos)
{
/* since this function recurses, it could be driven to stack overflow. */
check_stack_depth();

if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
{
ptr[*pos].left = 0;
Expand Down Expand Up @@ -340,8 +347,12 @@ queryin(char *buf)
errmsg("syntax error"),
errdetail("Empty query.")));

/* make finish struct */
if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("ltxtquery is too large")));
commonlen = COMPUTESIZE(state.num, state.sumlen);

query = (ltxtquery *) palloc(commonlen);
SET_VARSIZE(query, commonlen);
query->size = state.num;
Expand Down
30 changes: 28 additions & 2 deletions src/backend/utils/adt/geo_ops.c
Expand Up @@ -1366,6 +1366,7 @@ path_in(PG_FUNCTION_ARGS)
char *s;
int npts;
int size;
int base_size;
int depth = 0;

if ((npts = pair_count(str, ',')) <= 0)
Expand All @@ -1384,7 +1385,15 @@ path_in(PG_FUNCTION_ARGS)
depth++;
}

size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * npts;
base_size = sizeof(path->p[0]) * npts;
size = offsetof(PATH, p[0]) + base_size;

/* Check for integer overflow */
if (base_size / npts != sizeof(path->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));

path = (PATH *) palloc(size);

SET_VARSIZE(path, size);
Expand Down Expand Up @@ -3429,6 +3438,7 @@ poly_in(PG_FUNCTION_ARGS)
POLYGON *poly;
int npts;
int size;
int base_size;
int isopen;
char *s;

Expand All @@ -3437,7 +3447,15 @@ poly_in(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type polygon: \"%s\"", str)));

size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * npts;
base_size = sizeof(poly->p[0]) * npts;
size = offsetof(POLYGON, p[0]) + base_size;

/* Check for integer overflow */
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));

poly = (POLYGON *) palloc0(size); /* zero any holes */

SET_VARSIZE(poly, size);
Expand Down Expand Up @@ -4343,6 +4361,10 @@ path_poly(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("open path cannot be converted to polygon")));

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

Expand Down Expand Up @@ -4448,6 +4470,10 @@ poly_path(PG_FUNCTION_ARGS)
int size;
int i;

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

Expand Down
7 changes: 6 additions & 1 deletion src/backend/utils/adt/tsquery.c
Expand Up @@ -514,8 +514,13 @@ parse_tsquery(char *buf,
return query;
}

/* Pack the QueryItems in the final TSQuery struct to return to caller */
if (TSQUERY_TOO_BIG(list_length(state.polstr), state.sumlen))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("tsquery is too large")));
commonlen = COMPUTESIZE(list_length(state.polstr), state.sumlen);

/* Pack the QueryItems in the final TSQuery struct to return to caller */
query = (TSQuery) palloc0(commonlen);
SET_VARSIZE(query, commonlen);
query->size = list_length(state.polstr);
Expand Down
5 changes: 5 additions & 0 deletions src/backend/utils/adt/tsquery_util.c
Expand Up @@ -333,6 +333,11 @@ QTN2QT(QTNode *in)
QTN2QTState state;

cntsize(in, &sumlen, &nnode);

if (TSQUERY_TOO_BIG(nnode, sumlen))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("tsquery is too large")));
len = COMPUTESIZE(nnode, sumlen);

out = (TSQuery) palloc0(len);
Expand Down

0 comments on commit 31400a6

Please sign in to comment.