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

Commit 3b0f6a7

Browse files
committed
Prevent integer overflows in array subscripting calculations.
While we were (mostly) careful about ensuring that the dimensions of arrays aren't large enough to cause integer overflow, the lower bound values were generally not checked. This allows situations where lower_bound + dimension overflows an integer. It seems that that's harmless so far as array reading is concerned, except that array elements with subscripts notionally exceeding INT_MAX are inaccessible. However, it confuses various array-assignment logic, resulting in a potential for memory stomps. Fix by adding checks that array lower bounds aren't large enough to cause lower_bound + dimension to overflow. (Note: this results in disallowing cases where the last subscript position would be exactly INT_MAX. In principle we could probably allow that, but there's a lot of code that computes lower_bound + dimension and would need adjustment. It seems doubtful that it's worth the trouble/risk to allow it.) Somewhat independently of that, array_set_element() was careless about possible overflow when checking the subscript of a fixed-length array, creating a different route to memory stomps. Fix that too. Security: CVE-2021-32027
1 parent c5edbfe commit 3b0f6a7

File tree

5 files changed

+61
-16
lines changed

5 files changed

+61
-16
lines changed

src/backend/executor/execExprInterp.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2731,6 +2731,10 @@ ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
27312731
lbs[i] = elem_lbs[i - 1];
27322732
}
27332733

2734+
/* check for subscript overflow */
2735+
(void) ArrayGetNItems(ndims, dims);
2736+
ArrayCheckBounds(ndims, dims, lbs);
2737+
27342738
if (havenulls)
27352739
{
27362740
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);

src/backend/utils/adt/array_userfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ array_cat(PG_FUNCTION_ARGS)
411411

412412
/* Do this mainly for overflow checking */
413413
nitems = ArrayGetNItems(ndims, dims);
414+
ArrayCheckBounds(ndims, dims, lbs);
414415

415416
/* build the result array */
416417
ndatabytes = ndatabytes1 + ndatabytes2;

src/backend/utils/adt/arrayfuncs.c

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ array_in(PG_FUNCTION_ARGS)
371371

372372
/* This checks for overflow of the array dimensions */
373373
nitems = ArrayGetNItems(ndim, dim);
374+
ArrayCheckBounds(ndim, dim, lBound);
375+
374376
/* Empty array? */
375377
if (nitems == 0)
376378
PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
@@ -1320,24 +1322,11 @@ array_recv(PG_FUNCTION_ARGS)
13201322
{
13211323
dim[i] = pq_getmsgint(buf, 4);
13221324
lBound[i] = pq_getmsgint(buf, 4);
1323-
1324-
/*
1325-
* Check overflow of upper bound. (ArrayGetNItems() below checks that
1326-
* dim[i] >= 0)
1327-
*/
1328-
if (dim[i] != 0)
1329-
{
1330-
int ub = lBound[i] + dim[i] - 1;
1331-
1332-
if (lBound[i] > ub)
1333-
ereport(ERROR,
1334-
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1335-
errmsg("integer out of range")));
1336-
}
13371325
}
13381326

13391327
/* This checks for overflow of array dimensions */
13401328
nitems = ArrayGetNItems(ndim, dim);
1329+
ArrayCheckBounds(ndim, dim, lBound);
13411330

13421331
/*
13431332
* We arrange to look up info about element type, including its receive
@@ -2242,7 +2231,7 @@ array_set_element(Datum arraydatum,
22422231
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
22432232
errmsg("wrong number of array subscripts")));
22442233

2245-
if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2234+
if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
22462235
ereport(ERROR,
22472236
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
22482237
errmsg("array subscript out of range")));
@@ -2357,10 +2346,13 @@ array_set_element(Datum arraydatum,
23572346
}
23582347
}
23592348

2349+
/* This checks for overflow of the array dimensions */
2350+
newnitems = ArrayGetNItems(ndim, dim);
2351+
ArrayCheckBounds(ndim, dim, lb);
2352+
23602353
/*
23612354
* Compute sizes of items and areas to copy
23622355
*/
2363-
newnitems = ArrayGetNItems(ndim, dim);
23642356
if (newhasnulls)
23652357
overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
23662358
else
@@ -2615,6 +2607,13 @@ array_set_element_expanded(Datum arraydatum,
26152607
}
26162608
}
26172609

2610+
/* Check for overflow of the array dimensions */
2611+
if (dimschanged)
2612+
{
2613+
(void) ArrayGetNItems(ndim, dim);
2614+
ArrayCheckBounds(ndim, dim, lb);
2615+
}
2616+
26182617
/* Now we can calculate linear offset of target item in array */
26192618
offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
26202619

@@ -2933,6 +2932,7 @@ array_set_slice(Datum arraydatum,
29332932

29342933
/* Do this mainly to check for overflow */
29352934
nitems = ArrayGetNItems(ndim, dim);
2935+
ArrayCheckBounds(ndim, dim, lb);
29362936

29372937
/*
29382938
* Make sure source array has enough entries. Note we ignore the shape of
@@ -3347,7 +3347,9 @@ construct_md_array(Datum *elems,
33473347
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
33483348
ndims, MAXDIM)));
33493349

3350+
/* This checks for overflow of the array dimensions */
33503351
nelems = ArrayGetNItems(ndims, dims);
3352+
ArrayCheckBounds(ndims, dims, lbs);
33513353

33523354
/* if ndims <= 0 or any dims[i] == 0, return empty array */
33533355
if (nelems <= 0)
@@ -5421,6 +5423,10 @@ makeArrayResultArr(ArrayBuildStateArr *astate,
54215423
int dataoffset,
54225424
nbytes;
54235425

5426+
/* Check for overflow of the array dimensions */
5427+
(void) ArrayGetNItems(astate->ndims, astate->dims);
5428+
ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5429+
54245430
/* Compute required space */
54255431
nbytes = astate->nbytes;
54265432
if (astate->nullbitmap != NULL)
@@ -5850,7 +5856,9 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
58505856
lbsv = deflbs;
58515857
}
58525858

5859+
/* This checks for overflow of the array dimensions */
58535860
nitems = ArrayGetNItems(ndims, dimv);
5861+
ArrayCheckBounds(ndims, dimv, lbsv);
58545862

58555863
/* fast track for empty array */
58565864
if (nitems <= 0)

src/backend/utils/adt/arrayutils.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "postgres.h"
1717

1818
#include "catalog/pg_type.h"
19+
#include "common/int.h"
1920
#include "utils/array.h"
2021
#include "utils/builtins.h"
2122
#include "utils/memutils.h"
@@ -111,6 +112,36 @@ ArrayGetNItems(int ndim, const int *dims)
111112
return (int) ret;
112113
}
113114

115+
/*
116+
* Verify sanity of proposed lower-bound values for an array
117+
*
118+
* The lower-bound values must not be so large as to cause overflow when
119+
* calculating subscripts, e.g. lower bound 2147483640 with length 10
120+
* must be disallowed. We actually insist that dims[i] + lb[i] be
121+
* computable without overflow, meaning that an array with last subscript
122+
* equal to INT_MAX will be disallowed.
123+
*
124+
* It is assumed that the caller already called ArrayGetNItems, so that
125+
* overflowed (negative) dims[] values have been eliminated.
126+
*/
127+
void
128+
ArrayCheckBounds(int ndim, const int *dims, const int *lb)
129+
{
130+
int i;
131+
132+
for (i = 0; i < ndim; i++)
133+
{
134+
/* PG_USED_FOR_ASSERTS_ONLY prevents variable-isn't-read warnings */
135+
int32 sum PG_USED_FOR_ASSERTS_ONLY;
136+
137+
if (pg_add_s32_overflow(dims[i], lb[i], &sum))
138+
ereport(ERROR,
139+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
140+
errmsg("array lower bound is too large: %d",
141+
lb[i])));
142+
}
143+
}
144+
114145
/*
115146
* Compute ranges (sub-array dimensions) for an array slice
116147
*

src/include/utils/array.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ extern void array_free_iterator(ArrayIterator iterator);
438438
extern int ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx);
439439
extern int ArrayGetOffset0(int n, const int *tup, const int *scale);
440440
extern int ArrayGetNItems(int ndim, const int *dims);
441+
extern void ArrayCheckBounds(int ndim, const int *dims, const int *lb);
441442
extern void mda_get_range(int n, int *span, const int *st, const int *endp);
442443
extern void mda_get_prod(int n, const int *range, int *prod);
443444
extern void mda_get_offset_values(int n, int *dist, const int *prod, const int *span);

0 commit comments

Comments
 (0)