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

Commit f02b908

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 6206454 commit f02b908

File tree

5 files changed

+61
-16
lines changed

5 files changed

+61
-16
lines changed

src/backend/executor/execExprInterp.c

+4
Original file line numberDiff line numberDiff line change
@@ -2828,6 +2828,10 @@ ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
28282828
lbs[i] = elem_lbs[i - 1];
28292829
}
28302830

2831+
/* check for subscript overflow */
2832+
(void) ArrayGetNItems(ndims, dims);
2833+
ArrayCheckBounds(ndims, dims, lbs);
2834+
28312835
if (havenulls)
28322836
{
28332837
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);

src/backend/utils/adt/array_userfuncs.c

+1
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

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

373373
/* This checks for overflow of the array dimensions */
374374
nitems = ArrayGetNItems(ndim, dim);
375+
ArrayCheckBounds(ndim, dim, lBound);
376+
375377
/* Empty array? */
376378
if (nitems == 0)
377379
PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
@@ -1342,24 +1344,11 @@ array_recv(PG_FUNCTION_ARGS)
13421344
{
13431345
dim[i] = pq_getmsgint(buf, 4);
13441346
lBound[i] = pq_getmsgint(buf, 4);
1345-
1346-
/*
1347-
* Check overflow of upper bound. (ArrayGetNItems() below checks that
1348-
* dim[i] >= 0)
1349-
*/
1350-
if (dim[i] != 0)
1351-
{
1352-
int ub = lBound[i] + dim[i] - 1;
1353-
1354-
if (lBound[i] > ub)
1355-
ereport(ERROR,
1356-
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1357-
errmsg("integer out of range")));
1358-
}
13591347
}
13601348

13611349
/* This checks for overflow of array dimensions */
13621350
nitems = ArrayGetNItems(ndim, dim);
1351+
ArrayCheckBounds(ndim, dim, lBound);
13631352

13641353
/*
13651354
* We arrange to look up info about element type, including its receive
@@ -2265,7 +2254,7 @@ array_set_element(Datum arraydatum,
22652254
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
22662255
errmsg("wrong number of array subscripts")));
22672256

2268-
if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2257+
if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
22692258
ereport(ERROR,
22702259
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
22712260
errmsg("array subscript out of range")));
@@ -2380,10 +2369,13 @@ array_set_element(Datum arraydatum,
23802369
}
23812370
}
23822371

2372+
/* This checks for overflow of the array dimensions */
2373+
newnitems = ArrayGetNItems(ndim, dim);
2374+
ArrayCheckBounds(ndim, dim, lb);
2375+
23832376
/*
23842377
* Compute sizes of items and areas to copy
23852378
*/
2386-
newnitems = ArrayGetNItems(ndim, dim);
23872379
if (newhasnulls)
23882380
overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
23892381
else
@@ -2641,6 +2633,13 @@ array_set_element_expanded(Datum arraydatum,
26412633
}
26422634
}
26432635

2636+
/* Check for overflow of the array dimensions */
2637+
if (dimschanged)
2638+
{
2639+
(void) ArrayGetNItems(ndim, dim);
2640+
ArrayCheckBounds(ndim, dim, lb);
2641+
}
2642+
26442643
/* Now we can calculate linear offset of target item in array */
26452644
offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
26462645

@@ -2960,6 +2959,7 @@ array_set_slice(Datum arraydatum,
29602959

29612960
/* Do this mainly to check for overflow */
29622961
nitems = ArrayGetNItems(ndim, dim);
2962+
ArrayCheckBounds(ndim, dim, lb);
29632963

29642964
/*
29652965
* Make sure source array has enough entries. Note we ignore the shape of
@@ -3374,7 +3374,9 @@ construct_md_array(Datum *elems,
33743374
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
33753375
ndims, MAXDIM)));
33763376

3377+
/* This checks for overflow of the array dimensions */
33773378
nelems = ArrayGetNItems(ndims, dims);
3379+
ArrayCheckBounds(ndims, dims, lbs);
33783380

33793381
/* if ndims <= 0 or any dims[i] == 0, return empty array */
33803382
if (nelems <= 0)
@@ -5449,6 +5451,10 @@ makeArrayResultArr(ArrayBuildStateArr *astate,
54495451
int dataoffset,
54505452
nbytes;
54515453

5454+
/* Check for overflow of the array dimensions */
5455+
(void) ArrayGetNItems(astate->ndims, astate->dims);
5456+
ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5457+
54525458
/* Compute required space */
54535459
nbytes = astate->nbytes;
54545460
if (astate->nullbitmap != NULL)
@@ -5878,7 +5884,9 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
58785884
lbsv = deflbs;
58795885
}
58805886

5887+
/* This checks for overflow of the array dimensions */
58815888
nitems = ArrayGetNItems(ndims, dimv);
5889+
ArrayCheckBounds(ndims, dimv, lbsv);
58825890

58835891
/* fast track for empty array */
58845892
if (nitems <= 0)

src/backend/utils/adt/arrayutils.c

+31
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

+1
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ extern void array_free_iterator(ArrayIterator iterator);
443443
extern int ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx);
444444
extern int ArrayGetOffset0(int n, const int *tup, const int *scale);
445445
extern int ArrayGetNItems(int ndim, const int *dims);
446+
extern void ArrayCheckBounds(int ndim, const int *dims, const int *lb);
446447
extern void mda_get_range(int n, int *span, const int *st, const int *endp);
447448
extern void mda_get_prod(int n, const int *range, int *prod);
448449
extern void mda_get_offset_values(int n, int *dist, const int *prod, const int *span);

0 commit comments

Comments
 (0)