Skip to content

Commit 0c1caa4

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 28a1164 commit 0c1caa4

File tree

5 files changed

+57
-16
lines changed

5 files changed

+57
-16
lines changed

src/backend/executor/execQual.c

+4
Original file line numberDiff line numberDiff line change
@@ -3293,6 +3293,10 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
32933293
lbs[i] = elem_lbs[i - 1];
32943294
}
32953295

3296+
/* check for subscript overflow */
3297+
(void) ArrayGetNItems(ndims, dims);
3298+
ArrayCheckBounds(ndims, dims, lbs);
3299+
32963300
if (havenulls)
32973301
{
32983302
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);

src/backend/utils/adt/array_userfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ array_cat(PG_FUNCTION_ARGS)
416416

417417
/* Do this mainly for overflow checking */
418418
nitems = ArrayGetNItems(ndims, dims);
419+
ArrayCheckBounds(ndims, dims, lbs);
419420

420421
/* build the result array */
421422
ndatabytes = ndatabytes1 + ndatabytes2;

src/backend/utils/adt/arrayfuncs.c

+24-16
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,8 @@ array_in(PG_FUNCTION_ARGS)
370370

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

13381326
/* This checks for overflow of array dimensions */
13391327
nitems = ArrayGetNItems(ndim, dim);
1328+
ArrayCheckBounds(ndim, dim, lBound);
13401329

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

2244-
if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2233+
if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
22452234
ereport(ERROR,
22462235
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
22472236
errmsg("array subscript out of range")));
@@ -2356,10 +2345,13 @@ array_set_element(Datum arraydatum,
23562345
}
23572346
}
23582347

2348+
/* This checks for overflow of the array dimensions */
2349+
newnitems = ArrayGetNItems(ndim, dim);
2350+
ArrayCheckBounds(ndim, dim, lb);
2351+
23592352
/*
23602353
* Compute sizes of items and areas to copy
23612354
*/
2362-
newnitems = ArrayGetNItems(ndim, dim);
23632355
if (newhasnulls)
23642356
overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
23652357
else
@@ -2614,6 +2606,13 @@ array_set_element_expanded(Datum arraydatum,
26142606
}
26152607
}
26162608

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

@@ -2932,6 +2931,7 @@ array_set_slice(Datum arraydatum,
29322931

29332932
/* Do this mainly to check for overflow */
29342933
nitems = ArrayGetNItems(ndim, dim);
2934+
ArrayCheckBounds(ndim, dim, lb);
29352935

29362936
/*
29372937
* Make sure source array has enough entries. Note we ignore the shape of
@@ -3377,7 +3377,9 @@ construct_md_array(Datum *elems,
33773377
if (ndims == 0)
33783378
return construct_empty_array(elmtype);
33793379

3380+
/* This checks for overflow of the array dimensions */
33803381
nelems = ArrayGetNItems(ndims, dims);
3382+
ArrayCheckBounds(ndims, dims, lbs);
33813383

33823384
/* compute required space */
33833385
nbytes = 0;
@@ -5372,6 +5374,10 @@ makeArrayResultArr(ArrayBuildStateArr *astate,
53725374
int dataoffset,
53735375
nbytes;
53745376

5377+
/* Check for overflow of the array dimensions */
5378+
(void) ArrayGetNItems(astate->ndims, astate->dims);
5379+
ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5380+
53755381
/* Compute required space */
53765382
nbytes = astate->nbytes;
53775383
if (astate->nullbitmap != NULL)
@@ -5801,7 +5807,9 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
58015807
lbsv = deflbs;
58025808
}
58035809

5810+
/* This checks for overflow of the array dimensions */
58045811
nitems = ArrayGetNItems(ndims, dimv);
5812+
ArrayCheckBounds(ndims, dimv, lbsv);
58055813

58065814
/* fast track for empty array */
58075815
if (nitems <= 0)

src/backend/utils/adt/arrayutils.c

+27
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,33 @@ ArrayGetNItems(int ndim, const int *dims)
111111
return (int) ret;
112112
}
113113

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

src/include/utils/array.h

+1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ extern void array_free_iterator(ArrayIterator iterator);
465465
extern int ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx);
466466
extern int ArrayGetOffset0(int n, const int *tup, const int *scale);
467467
extern int ArrayGetNItems(int ndim, const int *dims);
468+
extern void ArrayCheckBounds(int ndim, const int *dims, const int *lb);
468469
extern void mda_get_range(int n, int *span, const int *st, const int *endp);
469470
extern void mda_get_prod(int n, const int *range, int *prod);
470471
extern void mda_get_offset_values(int n, int *dist, const int *prod, const int *span);

0 commit comments

Comments
 (0)