diff --git a/src/common/backend/utils/adt/array_userfuncs.cpp b/src/common/backend/utils/adt/array_userfuncs.cpp index 85e0bf4a0..7b5fa71ca 100644 --- a/src/common/backend/utils/adt/array_userfuncs.cpp +++ b/src/common/backend/utils/adt/array_userfuncs.cpp @@ -317,6 +317,7 @@ Datum array_cat(PG_FUNCTION_ARGS) /* Do this mainly for overflow checking */ nitems = ArrayGetNItems(ndims, dims); + ArrayCheckBounds(ndims, dims, lbs); /* build the result array */ ndatabytes = ndatabytes1 + ndatabytes2; diff --git a/src/common/backend/utils/adt/arrayfuncs.cpp b/src/common/backend/utils/adt/arrayfuncs.cpp index 60f8da6f0..507607469 100644 --- a/src/common/backend/utils/adt/arrayfuncs.cpp +++ b/src/common/backend/utils/adt/arrayfuncs.cpp @@ -328,6 +328,8 @@ Datum 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)); @@ -1224,21 +1226,11 @@ Datum array_recv(PG_FUNCTION_ARGS) (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("array size cannot be negative."))); } - - /* - * Check overflow of upper bound. (ArrayNItems() 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 @@ -2944,7 +2936,7 @@ ArrayType* array_set(ArrayType* array, int nSubscripts, const int* indx, Datum d if (nSubscripts != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); - if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen) + if (indx[0] < 0 || elmlen == 0 || indx[0] >= arraytyplen / elmlen) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array subscript out of range"))); if (isNull) @@ -3034,6 +3026,8 @@ ArrayType* array_set(ArrayType* array, int nSubscripts, const int* indx, Datum d * Compute sizes of items and areas to copy */ newnitems = ArrayGetNItems(ndim, dim); + ArrayCheckBounds(ndim, dim, lb); + if (newhasnulls) overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems); else @@ -3284,6 +3278,7 @@ ArrayType* array_set_slice(ArrayType* array, int nSubscripts, int* upperIndx, in /* 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 @@ -3694,7 +3689,8 @@ ArrayType* construct_md_array(Datum* elems, bool* nulls, int ndims, int* dims, c return construct_empty_array(elmtype); nelems = ArrayGetNItems(ndims, dims); - + ArrayCheckBounds(ndims, dims, lbs); + /* compute required space */ nbytes = 0; hasnulls = false; @@ -5455,6 +5451,7 @@ static ArrayType* array_fill_internal( return construct_empty_array(elmtype); nitems = ArrayGetNItems(ndims, dimv); + ArrayCheckBounds(ndims, dimv, lbsv); /* * We arrange to look up info about element type only once per series of diff --git a/src/common/backend/utils/adt/arrayutils.cpp b/src/common/backend/utils/adt/arrayutils.cpp index bb757a33a..b4dcfef09 100644 --- a/src/common/backend/utils/adt/arrayutils.cpp +++ b/src/common/backend/utils/adt/arrayutils.cpp @@ -17,6 +17,7 @@ #include "knl/knl_variable.h" #include "catalog/pg_type.h" +#include "common/int.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/memutils.h" @@ -100,6 +101,31 @@ int ArrayGetNItems(int ndim, const int* dims) return (int)ret; } +/* + * Verify sanity of proposed lower-bound values for an array + * + * The lower-bound values must not be so large as to cause overflow when + * calculating subscripts, e.g. lower bound 2147483640 with length 10 + * must be disallowed. We actually insist that dims[i] + lb[i] be + * computable without overflow, meaning that an array with last subscript + * equal to INT_MAX will be disallowed. + * + * It is assumed that the caller already called ArrayGetNItems, so that + * overflowed (negative) dims[] values have been eliminated. + */ +void ArrayCheckBounds(int ndim, const int *dims, const int *lb) +{ + int i = 0; + for (i = 0; i < ndim; i++) { + /* PG_USED_FOR_ASSERTS_ONLY prevents variable-isn't-read warnings */ + int32 sum PG_USED_FOR_ASSERTS_ONLY; + + if (pg_add_s32_overflow(dims[i], lb[i], &sum)) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("array lower bound is too large: %d", lb[i]))); + } +} + /* * Compute ranges (sub-array dimensions) for an array slice * diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 39152dc72..a04491daf 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -247,6 +247,7 @@ extern int ArrayCastAndSet(Datum src, int typlen, bool typbyval, char typalign, extern int ArrayGetOffset(int n, const int* dim, const int* lb, const int* indx); extern int ArrayGetOffset0(int n, const int* tup, const int* scale); extern int ArrayGetNItems(int ndim, const int* dims); +extern void ArrayCheckBounds(int ndim, const int *dims, const int *lb); extern void mda_get_range(int n, int* span, const int* st, const int* endp); extern void mda_get_prod(int n, const int* range, int* prod); extern void mda_get_offset_values(int n, int* dist, const int* prod, const int* span); diff --git a/src/test/regress/expected/plpgsql_array.out b/src/test/regress/expected/plpgsql_array.out index 475911b20..ae55d6311 100644 --- a/src/test/regress/expected/plpgsql_array.out +++ b/src/test/regress/expected/plpgsql_array.out @@ -561,25 +561,65 @@ declare end; drop table testtbl; +select array_cat( + array_cat( + array_extendnull( + array_cat('[2147483645:2147483647]={1,2,3}'::int[], array[4]), + 1 + ), + array_extendnull( + array_cat('[2147483645:2147483647]={10,20,30}'::int[], array[40]), + 1 + ) + ), + array[0,0,0,0,0,0,0,0,0] +) as result; +DECLARE +id_arr int[]; +BEGIN +id_arr[2047483630] = 1; +id_arr[2147483647] = 1; +id_arr[2047483641] = 1; +raise notice '%,%',id_arr[2147483644],id_arr[2147483645]; +END; +/ +ERROR: array lower bound is too large: 2147483645 +LINE 13: array_cat('[2147483645:2147483647]={1,2,3}'::int... + ^ +CONTEXT: referenced column: result +drop table if exists cve_2021_32027_tb; +NOTICE: table "cve_2021_32027_tb" does not exist, skipping +create table cve_2021_32027_tb (i int, p point); +insert into cve_2021_32027_tb(p) values('(1,1)'); +update cve_2021_32027_tb set p[2147483647] = 0 returning *; +ERROR: array subscript out of range +CONTEXT: referenced column: p +update cve_2021_32027_tb set p[268435456] = 0 returning *; +ERROR: array subscript out of range +CONTEXT: referenced column: p +update cve_2021_32027_tb set p[536870912] = 0 returning *; +ERROR: array subscript out of range +CONTEXT: referenced column: p -------------------------------------------------- ------------------ END OF TESTS ------------------ -------------------------------------------------- - +drop table if exists cve_2021_32027_tb; drop package if exists pckg_test; +NOTICE: drop cascades to function plpgsql_array.proc_test() drop procedure if exists pro1; drop function if exists functype; +NOTICE: function functype() does not exist, skipping drop function if exists myfunc; drop function if exists myarray_sort; drop table if exists tmp; drop table if exists customers; drop type if exists functype; +NOTICE: type "functype" does not exist, skipping drop type if exists mytype2; drop type if exists mytype; - -- clean up -- drop schema if exists plpgsql_array cascade; -NOTICE: drop cascades to function plpgsql_array.proc_test() -NOTICE: function functype() does not exist, skipping -NOTICE: type "functype" does not exist, skipping -NOTICE: drop cascades to function name_list(integer) +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to function name_list(integer) +drop cascades to table testtbl diff --git a/src/test/regress/sql/plpgsql_array.sql b/src/test/regress/sql/plpgsql_array.sql index b045aa94a..397cb4d52 100644 --- a/src/test/regress/sql/plpgsql_array.sql +++ b/src/test/regress/sql/plpgsql_array.sql @@ -451,11 +451,41 @@ declare end; drop table testtbl; +select array_cat( + array_cat( + array_extendnull( + array_cat('[2147483645:2147483647]={1,2,3}'::int[], array[4]), + 1 + ), + array_extendnull( + array_cat('[2147483645:2147483647]={10,20,30}'::int[], array[40]), + 1 + ) + ), + array[0,0,0,0,0,0,0,0,0] +) as result; + +DECLARE +id_arr int[]; +BEGIN +id_arr[2047483630] = 1; +id_arr[2147483647] = 1; +id_arr[2047483641] = 1; +raise notice '%,%',id_arr[2147483644],id_arr[2147483645]; +END; +/ + +drop table if exists cve_2021_32027_tb; +create table cve_2021_32027_tb (i int, p point); +insert into cve_2021_32027_tb(p) values('(1,1)'); +update cve_2021_32027_tb set p[2147483647] = 0 returning *; +update cve_2021_32027_tb set p[268435456] = 0 returning *; +update cve_2021_32027_tb set p[536870912] = 0 returning *; -------------------------------------------------- ------------------ END OF TESTS ------------------ -------------------------------------------------- - +drop table if exists cve_2021_32027_tb; drop package if exists pckg_test; drop procedure if exists pro1; drop function if exists functype;