diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index d6423e405f..9446d758ed 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -19,6 +19,7 @@ #include "access/htup_details.h" #include "catalog/pg_type.h" +#include "common/int.h" #include "funcapi.h" #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" @@ -2311,22 +2312,38 @@ array_set_element(Datum arraydatum, addedbefore = addedafter = 0; /* - * Check subscripts + * Check subscripts. We assume the existing subscripts passed + * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without + * overflow. But we must beware of other overflows in our calculations of + * new dim[] values. */ if (ndim == 1) { if (indx[0] < lb[0]) { - addedbefore = lb[0] - indx[0]; - dim[0] += addedbefore; + /* addedbefore = lb[0] - indx[0]; */ + /* dim[0] += addedbefore; */ + if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) || + pg_add_s32_overflow(dim[0], addedbefore, &dim[0])) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%d)", + (int) MaxArraySize))); lb[0] = indx[0]; if (addedbefore > 1) newhasnulls = true; /* will insert nulls */ } if (indx[0] >= (dim[0] + lb[0])) { - addedafter = indx[0] - (dim[0] + lb[0]) + 1; - dim[0] += addedafter; + /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */ + /* dim[0] += addedafter; */ + if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) || + pg_add_s32_overflow(addedafter, 1, &addedafter) || + pg_add_s32_overflow(dim[0], addedafter, &dim[0])) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%d)", + (int) MaxArraySize))); if (addedafter > 1) newhasnulls = true; /* will insert nulls */ } @@ -2569,14 +2586,23 @@ array_set_element_expanded(Datum arraydatum, addedbefore = addedafter = 0; /* - * Check subscripts (this logic matches original array_set_element) + * Check subscripts (this logic must match array_set_element). We assume + * the existing subscripts passed ArrayCheckBounds, so that dim[i] + lb[i] + * can be computed without overflow. But we must beware of other + * overflows in our calculations of new dim[] values. */ if (ndim == 1) { if (indx[0] < lb[0]) { - addedbefore = lb[0] - indx[0]; - dim[0] += addedbefore; + /* addedbefore = lb[0] - indx[0]; */ + /* dim[0] += addedbefore; */ + if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) || + pg_add_s32_overflow(dim[0], addedbefore, &dim[0])) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%d)", + (int) MaxArraySize))); lb[0] = indx[0]; dimschanged = true; if (addedbefore > 1) @@ -2584,8 +2610,15 @@ array_set_element_expanded(Datum arraydatum, } if (indx[0] >= (dim[0] + lb[0])) { - addedafter = indx[0] - (dim[0] + lb[0]) + 1; - dim[0] += addedafter; + /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */ + /* dim[0] += addedafter; */ + if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) || + pg_add_s32_overflow(addedafter, 1, &addedafter) || + pg_add_s32_overflow(dim[0], addedafter, &dim[0])) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%d)", + (int) MaxArraySize))); dimschanged = true; if (addedafter > 1) newhasnulls = true; /* will insert nulls */ @@ -2867,7 +2900,10 @@ array_set_slice(Datum arraydatum, addedbefore = addedafter = 0; /* - * Check subscripts + * Check subscripts. We assume the existing subscripts passed + * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without + * overflow. But we must beware of other overflows in our calculations of + * new dim[] values. */ if (ndim == 1) { @@ -2882,18 +2918,31 @@ array_set_slice(Datum arraydatum, errmsg("upper bound cannot be less than lower bound"))); if (lowerIndx[0] < lb[0]) { - if (upperIndx[0] < lb[0] - 1) - newhasnulls = true; /* will insert nulls */ - addedbefore = lb[0] - lowerIndx[0]; - dim[0] += addedbefore; + /* addedbefore = lb[0] - lowerIndx[0]; */ + /* dim[0] += addedbefore; */ + if (pg_sub_s32_overflow(lb[0], lowerIndx[0], &addedbefore) || + pg_add_s32_overflow(dim[0], addedbefore, &dim[0])) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%d)", + (int) MaxArraySize))); lb[0] = lowerIndx[0]; + if (addedbefore > 1) + newhasnulls = true; /* will insert nulls */ } if (upperIndx[0] >= (dim[0] + lb[0])) { - if (lowerIndx[0] > (dim[0] + lb[0])) + /* addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; */ + /* dim[0] += addedafter; */ + if (pg_sub_s32_overflow(upperIndx[0], dim[0] + lb[0], &addedafter) || + pg_add_s32_overflow(addedafter, 1, &addedafter) || + pg_add_s32_overflow(dim[0], addedafter, &dim[0])) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%d)", + (int) MaxArraySize))); + if (addedafter > 1) newhasnulls = true; /* will insert nulls */ - addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; - dim[0] += addedafter; } } else diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c index 41c5f4a34d..e6b9b7a36d 100644 --- a/src/backend/utils/adt/arrayutils.c +++ b/src/backend/utils/adt/arrayutils.c @@ -64,10 +64,6 @@ ArrayGetOffset0(int n, const int *tup, const int *scale) * This must do overflow checking, since it is used to validate that a user * dimensionality request doesn't overflow what we can handle. * - * We limit array sizes to at most about a quarter billion elements, - * so that it's not necessary to check for overflow in quite so many - * places --- for instance when palloc'ing Datum arrays. - * * The multiplication overflow check only works on machines that have int64 * arithmetic, but that is nearly all platforms these days, and doing check * divides for those that don't seems way too expensive. @@ -78,8 +74,6 @@ ArrayGetNItems(int ndim, const int *dims) int32 ret; int i; -#define MaxArraySize ((Size) (MaxAllocSize / sizeof(Datum))) - if (ndim <= 0) return 0; ret = 1; diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 0dcc8851cc..3a3fc0f910 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -69,6 +69,13 @@ struct ExprState; struct ExprContext; +/* + * Maximum number of elements in an array. We limit this to at most about a + * quarter billion elements, so that it's not necessary to check for overflow + * in quite so many places --- for instance when palloc'ing Datum arrays. + */ +#define MaxArraySize ((Size) (MaxAllocSize / sizeof(Datum))) + /* * Arrays are varlena objects, so must meet the varlena convention that * the first int32 of the object contains the total object size in bytes. diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index db46a965c5..5042664c15 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -1359,6 +1359,23 @@ insert into arr_pk_tbl(pk, f1[1:2]) values (1, '{6,7,8}') on conflict (pk) -- then you didn't get an indexscan plan, and something is busted. reset enable_seqscan; reset enable_bitmapscan; +-- test subscript overflow detection +-- The normal error message includes a platform-dependent limit, +-- so suppress it to avoid needing multiple expected-files. +\set VERBOSITY sqlstate +insert into arr_pk_tbl values(10, '[-2147483648:-2147483647]={1,2}'); +update arr_pk_tbl set f1[2147483647] = 42 where pk = 10; +ERROR: 54000 +update arr_pk_tbl set f1[2147483646:2147483647] = array[4,2] where pk = 10; +ERROR: 54000 +-- also exercise the expanded-array case +do $$ declare a int[]; +begin + a := '[-2147483648:-2147483647]={1,2}'::int[]; + a[2147483647] := 42; +end $$; +ERROR: 54000 +\set VERBOSITY default -- test [not] (like|ilike) (any|all) (...) select 'foo' like any (array['%a', '%o']); -- t ?column? diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index bf9319c704..46bc864cd6 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -409,6 +409,25 @@ insert into arr_pk_tbl(pk, f1[1:2]) values (1, '{6,7,8}') on conflict (pk) reset enable_seqscan; reset enable_bitmapscan; +-- test subscript overflow detection + +-- The normal error message includes a platform-dependent limit, +-- so suppress it to avoid needing multiple expected-files. +\set VERBOSITY sqlstate + +insert into arr_pk_tbl values(10, '[-2147483648:-2147483647]={1,2}'); +update arr_pk_tbl set f1[2147483647] = 42 where pk = 10; +update arr_pk_tbl set f1[2147483646:2147483647] = array[4,2] where pk = 10; + +-- also exercise the expanded-array case +do $$ declare a int[]; +begin + a := '[-2147483648:-2147483647]={1,2}'::int[]; + a[2147483647] := 42; +end $$; + +\set VERBOSITY default + -- test [not] (like|ilike) (any|all) (...) select 'foo' like any (array['%a', '%o']); -- t select 'foo' like any (array['%a', '%b']); -- f