diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2021-05-10 10:44:38 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2021-05-10 10:44:38 -0400 |
commit | f02b9085ad2f6fefd9c5cdf85579cb9f0ff0f0ea (patch) | |
tree | 5f62efd8e2156c24cb6f4bc25b4f22a991ab284e /src/backend/utils/adt/arrayfuncs.c | |
parent | 6206454bdac1ccd6f6ed9d811e1a1139e663a8b9 (diff) | |
download | postgresql-f02b9085ad2f6fefd9c5cdf85579cb9f0ff0f0ea.tar.gz postgresql-f02b9085ad2f6fefd9c5cdf85579cb9f0ff0f0ea.zip |
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
Diffstat (limited to 'src/backend/utils/adt/arrayfuncs.c')
-rw-r--r-- | src/backend/utils/adt/arrayfuncs.c | 40 |
1 files changed, 24 insertions, 16 deletions
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 17a16b4c5cc..04d487c5442 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -372,6 +372,8 @@ array_in(PG_FUNCTION_ARGS) /* This checks for overflow of the array dimensions */ nitems = ArrayGetNItems(ndim, dim); + ArrayCheckBounds(ndim, dim, lBound); + /* Empty array? */ if (nitems == 0) PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type)); @@ -1342,24 +1344,11 @@ array_recv(PG_FUNCTION_ARGS) { dim[i] = pq_getmsgint(buf, 4); lBound[i] = pq_getmsgint(buf, 4); - - /* - * Check overflow of upper bound. (ArrayGetNItems() below checks that - * dim[i] >= 0) - */ - if (dim[i] != 0) - { - int ub = lBound[i] + dim[i] - 1; - - if (lBound[i] > ub) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); - } } /* This checks for overflow of array dimensions */ nitems = ArrayGetNItems(ndim, dim); + ArrayCheckBounds(ndim, dim, lBound); /* * We arrange to look up info about element type, including its receive @@ -2265,7 +2254,7 @@ array_set_element(Datum arraydatum, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); - if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen) + if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array subscript out of range"))); @@ -2380,10 +2369,13 @@ array_set_element(Datum arraydatum, } } + /* This checks for overflow of the array dimensions */ + newnitems = ArrayGetNItems(ndim, dim); + ArrayCheckBounds(ndim, dim, lb); + /* * Compute sizes of items and areas to copy */ - newnitems = ArrayGetNItems(ndim, dim); if (newhasnulls) overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems); else @@ -2641,6 +2633,13 @@ array_set_element_expanded(Datum arraydatum, } } + /* Check for overflow of the array dimensions */ + if (dimschanged) + { + (void) ArrayGetNItems(ndim, dim); + ArrayCheckBounds(ndim, dim, lb); + } + /* Now we can calculate linear offset of target item in array */ offset = ArrayGetOffset(nSubscripts, dim, lb, indx); @@ -2960,6 +2959,7 @@ array_set_slice(Datum arraydatum, /* Do this mainly to check for overflow */ nitems = ArrayGetNItems(ndim, dim); + ArrayCheckBounds(ndim, dim, lb); /* * Make sure source array has enough entries. Note we ignore the shape of @@ -3374,7 +3374,9 @@ construct_md_array(Datum *elems, errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", ndims, MAXDIM))); + /* This checks for overflow of the array dimensions */ nelems = ArrayGetNItems(ndims, dims); + ArrayCheckBounds(ndims, dims, lbs); /* if ndims <= 0 or any dims[i] == 0, return empty array */ if (nelems <= 0) @@ -5449,6 +5451,10 @@ makeArrayResultArr(ArrayBuildStateArr *astate, int dataoffset, nbytes; + /* Check for overflow of the array dimensions */ + (void) ArrayGetNItems(astate->ndims, astate->dims); + ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs); + /* Compute required space */ nbytes = astate->nbytes; if (astate->nullbitmap != NULL) @@ -5878,7 +5884,9 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, lbsv = deflbs; } + /* This checks for overflow of the array dimensions */ nitems = ArrayGetNItems(ndims, dimv); + ArrayCheckBounds(ndims, dimv, lbsv); /* fast track for empty array */ if (nitems <= 0) |