aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/utils/adt/arrayfuncs.c85
-rw-r--r--src/backend/utils/adt/arrayutils.c6
-rw-r--r--src/include/utils/array.h7
-rw-r--r--src/test/regress/expected/arrays.out17
-rw-r--r--src/test/regress/sql/arrays.sql19
5 files changed, 110 insertions, 24 deletions
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 9000f83a836..c5336940dab 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"
@@ -2358,22 +2359,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 */
}
@@ -2619,14 +2636,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)
@@ -2634,8 +2660,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 */
@@ -2918,7 +2951,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)
{
@@ -2933,18 +2969,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 62152f79f53..aed799234cd 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.
@@ -88,8 +84,6 @@ ArrayGetNItemsSafe(int ndim, const int *dims, struct Node *escontext)
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 b13dfb345ef..e6c8d88d9f2 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -75,6 +75,13 @@ struct ExprContext;
#define MAXDIM 6
/*
+ * 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.
* Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 70643914688..957498432d6 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1418,6 +1418,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 f1375621e0c..daf805c3827 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -438,6 +438,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