On Fri, Jul 19, 2024 at 2:45 PM Nathan Bossart <nathandboss...@gmail.com> wrote: > > + /* dim[i] = 1 + upperIndx[i] - lowerIndx[i]; */ > + if (pg_add_s32_overflow(1, upperIndx[i], &dim[i])) > + ereport(ERROR, > + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), > + errmsg("array upper bound is too large: %d", > + upperIndx[i]))); > + if (pg_sub_s32_overflow(dim[i], lowerIndx[i], &dim[i])) > + ereport(ERROR, > + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), > + errmsg("array size exceeds the maximum allowed (%d)", > + (int) MaxArraySize))); > > I think the problem with fixing it this way is that it prohibits more than > is necessary.
My understanding is that 2147483647 (INT32_MAX) is not a valid upper bound, which is what the first overflow check is checking. Any query of the form `INSERT INTO arroverflowtest(i[<lb>:2147483647]) VALUES ('{...}');` will fail with an error of `ERROR: array lower bound is too large: <lb>` The reason is the following bounds check found in arrayutils.c /* * 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) { (void) ArrayCheckBoundsSafe(ndim, dims, lb, NULL); } /* * This entry point can return the error into an ErrorSaveContext * instead of throwing an exception. */ bool ArrayCheckBoundsSafe(int ndim, const int *dims, const int *lb, struct Node *escontext) { int i; 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)) ereturn(escontext, false, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("array lower bound is too large: %d", lb[i]))); } return true; } Specifically "We actually insist that dims[i] + lb[i] be computable without overflow, meaning that an array with last subscript equal to INT32_MAX will be disallowed." If the upper bound is INT32_MAX, then there's no lower bound where (lower_bound + size) won't overflow. It might be possible to remove this restriction, but it's probably easier to keep it. > An easy way to deal with this problem is to first perform the calculation > with everything cast to an int64. Before setting dim[i], you'd check that > the result is in [PG_INT32_MIN, PG_INT32_MAX] and fail if needed. > > int64 newdim; > > ... > > newdim = (int64) 1 + (int64) upperIndx[i] - (int64) lowerIndx[i]; > if (unlikely(newdim < PG_INT32_MIN || newdim > PG_INT32_MAX)) > ereport(ERROR, > ... > dim[i] = (int32) newdim; I've rebased my patches and updated 0002 with this approach if this is still the approach you want to go with. I went with the array size too large error for similar reasons as the previous version of the patch. Since the patches have been renumbered, here's an overview of their status: - 0001 is reviewed and waiting for v18. - 0002 is under review and a bug fix. - 0003 needs review. Thanks, Joseph Koshakow
From 6996c17b06d4b7c98130e5c70c368d08ea1ccc80 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow <kosh...@gmail.com> Date: Sat, 6 Jul 2024 14:35:00 -0400 Subject: [PATCH 2/3] Remove overflow from array_set_slice This commit removes an overflow from array_set_slice that allows seting absurd slice ranges. --- src/backend/utils/adt/arrayfuncs.c | 10 +++++++++- src/test/regress/expected/arrays.out | 14 ++++++++++++++ src/test/regress/sql/arrays.sql | 9 +++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index d6641b570d..e81aea4d19 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -2880,6 +2880,8 @@ array_set_slice(Datum arraydatum, for (i = 0; i < nSubscripts; i++) { + int64 newdim; + if (!upperProvided[i] || !lowerProvided[i]) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), @@ -2887,7 +2889,13 @@ array_set_slice(Datum arraydatum, errdetail("When assigning to a slice of an empty array value," " slice boundaries must be fully specified."))); - dim[i] = 1 + upperIndx[i] - lowerIndx[i]; + newdim = (int64) 1 + (int64) upperIndx[i] - (int64) lowerIndx[i]; + if (unlikely(newdim < (int64) PG_INT32_MIN || newdim > (int64) PG_INT32_MAX)) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%d)", + (int) MaxArraySize))); + dim[i] = newdim; lb[i] = lowerIndx[i]; } diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 23404982f7..8014a492fc 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -2699,3 +2699,17 @@ SELECT array_sample('{1,2,3,4,5,6}'::int[], -1); -- fail ERROR: sample size must be between 0 and 6 SELECT array_sample('{1,2,3,4,5,6}'::int[], 7); --fail ERROR: sample size must be between 0 and 6 +-- Test for overflow in array slicing +CREATE temp table arroverflowtest (i int[]); +INSERT INTO arroverflowtest(i[-2147483648:2147483647]) VALUES ('{}'); +ERROR: array size exceeds the maximum allowed (134217727) +INSERT INTO arroverflowtest(i[1:2147483647]) VALUES ('{}'); +ERROR: array size exceeds the maximum allowed (134217727) +INSERT INTO arroverflowtest(i[2147483647:2147483647]) VALUES ('{}'); +ERROR: source array too small +INSERT INTO arroverflowtest(i[2147483647:2147483647]) VALUES ('{1}'); +ERROR: array lower bound is too large: 2147483647 +INSERT INTO arroverflowtest(i[2147483646:2147483647]) VALUES ('{1,2}'); +ERROR: array lower bound is too large: 2147483646 +INSERT INTO arroverflowtest(i[10:-2147483648]) VALUES ('{}'); +ERROR: array size exceeds the maximum allowed (134217727) diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index 50aa539fdc..fbadcd9f26 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -825,3 +825,12 @@ SELECT array_dims(array_sample('[-1:2][2:3]={{1,2},{3,NULL},{5,6},{7,8}}'::int[] SELECT array_dims(array_sample('{{{1,2},{3,NULL}},{{5,6},{7,8}},{{9,10},{11,12}}}'::int[], 2)); SELECT array_sample('{1,2,3,4,5,6}'::int[], -1); -- fail SELECT array_sample('{1,2,3,4,5,6}'::int[], 7); --fail + +-- Test for overflow in array slicing +CREATE temp table arroverflowtest (i int[]); +INSERT INTO arroverflowtest(i[-2147483648:2147483647]) VALUES ('{}'); +INSERT INTO arroverflowtest(i[1:2147483647]) VALUES ('{}'); +INSERT INTO arroverflowtest(i[2147483647:2147483647]) VALUES ('{}'); +INSERT INTO arroverflowtest(i[2147483647:2147483647]) VALUES ('{1}'); +INSERT INTO arroverflowtest(i[2147483646:2147483647]) VALUES ('{1,2}'); +INSERT INTO arroverflowtest(i[10:-2147483648]) VALUES ('{}'); -- 2.34.1
From d07a6a7f2b9cca2293ff004076e69b30f63e5d30 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow <kosh...@gmail.com> Date: Sat, 8 Jun 2024 22:16:46 -0400 Subject: [PATCH 1/3] Remove dependence on integer wrapping This commit updates various parts of the code to no longer rely on integer wrapping for correctness. Not all compilers support -fwrapv, so it's best not to rely on it. --- src/backend/utils/adt/cash.c | 7 +- src/backend/utils/adt/numeric.c | 5 +- src/backend/utils/adt/numutils.c | 34 ++++--- src/backend/utils/adt/timestamp.c | 28 +----- src/include/common/int.h | 105 +++++++++++++++++++++ src/interfaces/ecpg/pgtypeslib/timestamp.c | 11 +-- src/test/regress/expected/timestamp.out | 13 +++ src/test/regress/expected/timestamptz.out | 13 +++ src/test/regress/sql/timestamp.sql | 4 + src/test/regress/sql/timestamptz.sql | 4 + 10 files changed, 169 insertions(+), 55 deletions(-) diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c index b20c358486..52687dbf7b 100644 --- a/src/backend/utils/adt/cash.c +++ b/src/backend/utils/adt/cash.c @@ -429,8 +429,11 @@ cash_out(PG_FUNCTION_ARGS) if (value < 0) { - /* make the amount positive for digit-reconstruction loop */ - value = -value; + /* + * make the amount positive for digit-reconstruction loop, we can + * leave INT64_MIN unchanged + */ + pg_neg_s64_overflow(value, &value); /* set up formatting data */ signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-"; sign_posn = lconvert->n_sign_posn; diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index d0f0923710..38965b4023 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -8114,15 +8114,14 @@ int64_to_numericvar(int64 val, NumericVar *var) /* int64 can require at most 19 decimal digits; add one for safety */ alloc_var(var, 20 / DEC_DIGITS); + uval = pg_abs_s64(val); if (val < 0) { var->sign = NUMERIC_NEG; - uval = -val; } else { var->sign = NUMERIC_POS; - uval = val; } var->dscale = 0; if (val == 0) @@ -11443,7 +11442,7 @@ power_var_int(const NumericVar *base, int exp, int exp_dscale, * Now we can proceed with the multiplications. */ neg = (exp < 0); - mask = abs(exp); + mask = pg_abs_s32(exp); init_var(&base_prod); set_var_from_var(base, &base_prod); diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index adc1e8a4cb..a3d7d6bf01 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -18,6 +18,7 @@ #include <limits.h> #include <ctype.h> +#include "common/int.h" #include "port/pg_bitutils.h" #include "utils/builtins.h" @@ -131,6 +132,7 @@ pg_strtoint16_safe(const char *s, Node *escontext) uint16 tmp = 0; bool neg = false; unsigned char digit; + int16 result; /* * The majority of cases are likely to be base-10 digits without any @@ -190,10 +192,9 @@ pg_strtoint16_safe(const char *s, Node *escontext) if (neg) { - /* check the negative equivalent will fit without overflowing */ - if (unlikely(tmp > (uint16) (-(PG_INT16_MIN + 1)) + 1)) + if (pg_neg_u16_overflow(tmp, &result)) goto out_of_range; - return -((int16) tmp); + return result; } if (unlikely(tmp > PG_INT16_MAX)) @@ -333,10 +334,9 @@ slow: if (neg) { - /* check the negative equivalent will fit without overflowing */ - if (tmp > (uint16) (-(PG_INT16_MIN + 1)) + 1) + if (pg_neg_u16_overflow(tmp, &result)) goto out_of_range; - return -((int16) tmp); + return result; } if (tmp > PG_INT16_MAX) @@ -393,6 +393,7 @@ pg_strtoint32_safe(const char *s, Node *escontext) uint32 tmp = 0; bool neg = false; unsigned char digit; + int32 result; /* * The majority of cases are likely to be base-10 digits without any @@ -452,10 +453,9 @@ pg_strtoint32_safe(const char *s, Node *escontext) if (neg) { - /* check the negative equivalent will fit without overflowing */ - if (unlikely(tmp > (uint32) (-(PG_INT32_MIN + 1)) + 1)) + if (pg_neg_u32_overflow(tmp, &result)) goto out_of_range; - return -((int32) tmp); + return result; } if (unlikely(tmp > PG_INT32_MAX)) @@ -595,10 +595,9 @@ slow: if (neg) { - /* check the negative equivalent will fit without overflowing */ - if (tmp > (uint32) (-(PG_INT32_MIN + 1)) + 1) + if (pg_neg_u32_overflow(tmp, &result)) goto out_of_range; - return -((int32) tmp); + return result; } if (tmp > PG_INT32_MAX) @@ -655,6 +654,7 @@ pg_strtoint64_safe(const char *s, Node *escontext) uint64 tmp = 0; bool neg = false; unsigned char digit; + int64 result; /* * The majority of cases are likely to be base-10 digits without any @@ -714,10 +714,9 @@ pg_strtoint64_safe(const char *s, Node *escontext) if (neg) { - /* check the negative equivalent will fit without overflowing */ - if (unlikely(tmp > (uint64) (-(PG_INT64_MIN + 1)) + 1)) + if (pg_neg_u64_overflow(tmp, &result)) goto out_of_range; - return -((int64) tmp); + return result; } if (unlikely(tmp > PG_INT64_MAX)) @@ -857,10 +856,9 @@ slow: if (neg) { - /* check the negative equivalent will fit without overflowing */ - if (tmp > (uint64) (-(PG_INT64_MIN + 1)) + 1) + if (pg_neg_u64_overflow(tmp, &result)) goto out_of_range; - return -((int64) tmp); + return result; } if (tmp > PG_INT64_MAX) diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 69fe7860ed..c76793f72d 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -618,19 +618,8 @@ make_timestamp_internal(int year, int month, int day, time = (((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE) * USECS_PER_SEC) + (int64) rint(sec * USECS_PER_SEC); - result = date * USECS_PER_DAY + time; - /* check for major overflow */ - if ((result - time) / USECS_PER_DAY != date) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g", - year, month, day, - hour, min, sec))); - - /* check for just-barely overflow (okay except time-of-day wraps) */ - /* caution: we want to allow 1999-12-31 24:00:00 */ - if ((result < 0 && date > 0) || - (result > 0 && date < -1)) + if (pg_mul_s64_overflow(date, USECS_PER_DAY, &result) || + pg_add_s64_overflow(result, time, &result)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g", @@ -2010,17 +1999,8 @@ tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp *result) date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec); - *result = date * USECS_PER_DAY + time; - /* check for major overflow */ - if ((*result - time) / USECS_PER_DAY != date) - { - *result = 0; /* keep compiler quiet */ - return -1; - } - /* check for just-barely overflow (okay except time-of-day wraps) */ - /* caution: we want to allow 1999-12-31 24:00:00 */ - if ((*result < 0 && date > 0) || - (*result > 0 && date < -1)) + if (pg_mul_s64_overflow(date, USECS_PER_DAY, result) || + pg_add_s64_overflow(*result, time, result)) { *result = 0; /* keep compiler quiet */ return -1; diff --git a/src/include/common/int.h b/src/include/common/int.h index 7fc046e78a..3065d586d4 100644 --- a/src/include/common/int.h +++ b/src/include/common/int.h @@ -154,6 +154,23 @@ pg_mul_s32_overflow(int32 a, int32 b, int32 *result) #endif } +static inline uint32 +pg_abs_s32(int32 a) +{ + if (unlikely(a == PG_INT32_MIN)) + { + return ((uint32) PG_INT32_MAX) + 1; + } + else if (a < 0) + { + return (uint32) -a; + } + else + { + return (uint32) a; + } +} + /* * INT64 */ @@ -258,6 +275,37 @@ pg_mul_s64_overflow(int64 a, int64 b, int64 *result) #endif } +static inline bool +pg_neg_s64_overflow(int64 a, int64 *result) +{ + if (unlikely(a == PG_INT64_MIN)) + { + return true; + } + else + { + *result = -a; + return false; + } +} + +static inline uint64 +pg_abs_s64(int64 a) +{ + if (unlikely(a == PG_INT64_MIN)) + { + return ((uint64) PG_INT64_MAX) + 1; + } + else if (a < 0) + { + return (uint64) -a; + } + else + { + return (uint64) a; + } +} + /*------------------------------------------------------------------------ * Overflow routines for unsigned integers *------------------------------------------------------------------------ @@ -318,6 +366,25 @@ pg_mul_u16_overflow(uint16 a, uint16 b, uint16 *result) #endif } +static inline bool +pg_neg_u16_overflow(uint16 a, int16 *result) +{ + if (unlikely(a > ((uint16) PG_INT16_MAX) + 1)) + { + return true; + } + else if (unlikely(a == ((uint16) PG_INT16_MAX) + 1)) + { + *result = PG_INT16_MIN; + return false; + } + else + { + *result = -((int16) a); + return false; + } +} + /* * INT32 */ @@ -373,6 +440,25 @@ pg_mul_u32_overflow(uint32 a, uint32 b, uint32 *result) #endif } +static inline bool +pg_neg_u32_overflow(uint32 a, int32 *result) +{ + if (unlikely(a > ((uint32) PG_INT32_MAX) + 1)) + { + return true; + } + else if (unlikely(a == ((uint32) PG_INT32_MAX) + 1)) + { + *result = PG_INT32_MIN; + return false; + } + else + { + *result = -((int32) a); + return false; + } +} + /* * UINT64 */ @@ -438,6 +524,25 @@ pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *result) #endif } +static inline bool +pg_neg_u64_overflow(uint64 a, int64 *result) +{ + if (unlikely(a > ((uint64) PG_INT64_MAX) + 1)) + { + return true; + } + else if (unlikely(a == ((uint64) PG_INT64_MAX) + 1)) + { + *result = PG_INT64_MIN; + return false; + } + else + { + *result = -((int64) a); + return false; + } +} + /*------------------------------------------------------------------------ * * Comparison routines for integer types. diff --git a/src/interfaces/ecpg/pgtypeslib/timestamp.c b/src/interfaces/ecpg/pgtypeslib/timestamp.c index f1b143fbd2..93d4cc323d 100644 --- a/src/interfaces/ecpg/pgtypeslib/timestamp.c +++ b/src/interfaces/ecpg/pgtypeslib/timestamp.c @@ -11,6 +11,7 @@ #error -ffast-math is known to break this code #endif +#include "common/int.h" #include "dt.h" #include "pgtypes_date.h" #include "pgtypes_timestamp.h" @@ -48,14 +49,8 @@ tm2timestamp(struct tm *tm, fsec_t fsec, int *tzp, timestamp * result) dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec); - *result = (dDate * USECS_PER_DAY) + time; - /* check for major overflow */ - if ((*result - time) / USECS_PER_DAY != dDate) - return -1; - /* check for just-barely overflow (okay except time-of-day wraps) */ - /* caution: we want to allow 1999-12-31 24:00:00 */ - if ((*result < 0 && dDate > 0) || - (*result > 0 && dDate < -1)) + if (pg_mul_s64_overflow(dDate, USECS_PER_DAY, result) || + pg_add_s64_overflow(*result, time, result)) return -1; if (tzp != NULL) *result = dt2local(*result, -(*tzp)); diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index cf337da517..e287260051 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -2201,3 +2201,16 @@ select age(timestamp '-infinity', timestamp 'infinity'); select age(timestamp '-infinity', timestamp '-infinity'); ERROR: interval out of range +-- test timestamp near POSTGRES_EPOCH_JDATE +select timestamp '1999-12-31 24:00:00'; + timestamp +-------------------------- + Sat Jan 01 00:00:00 2000 +(1 row) + +select make_timestamp(1999, 12, 31, 24, 0, 0); + make_timestamp +-------------------------- + Sat Jan 01 00:00:00 2000 +(1 row) + diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index bfb3825ff6..d01d174983 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -3286,3 +3286,16 @@ SELECT age(timestamptz '-infinity', timestamptz 'infinity'); SELECT age(timestamptz '-infinity', timestamptz '-infinity'); ERROR: interval out of range +-- test timestamp near POSTGRES_EPOCH_JDATE +select timestamptz '1999-12-31 24:00:00'; + timestamptz +------------------------------ + Sat Jan 01 00:00:00 2000 PST +(1 row) + +select make_timestamptz(1999, 12, 31, 24, 0, 0); + make_timestamptz +------------------------------ + Sat Jan 01 00:00:00 2000 PST +(1 row) + diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql index 820ef7752a..748469576d 100644 --- a/src/test/regress/sql/timestamp.sql +++ b/src/test/regress/sql/timestamp.sql @@ -424,3 +424,7 @@ select age(timestamp 'infinity', timestamp 'infinity'); select age(timestamp 'infinity', timestamp '-infinity'); select age(timestamp '-infinity', timestamp 'infinity'); select age(timestamp '-infinity', timestamp '-infinity'); + +-- test timestamp near POSTGRES_EPOCH_JDATE +select timestamp '1999-12-31 24:00:00'; +select make_timestamp(1999, 12, 31, 24, 0, 0); diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index ccfd90d646..c71d5489b4 100644 --- a/src/test/regress/sql/timestamptz.sql +++ b/src/test/regress/sql/timestamptz.sql @@ -668,3 +668,7 @@ SELECT age(timestamptz 'infinity', timestamptz 'infinity'); SELECT age(timestamptz 'infinity', timestamptz '-infinity'); SELECT age(timestamptz '-infinity', timestamptz 'infinity'); SELECT age(timestamptz '-infinity', timestamptz '-infinity'); + +-- test timestamp near POSTGRES_EPOCH_JDATE +select timestamptz '1999-12-31 24:00:00'; +select make_timestamptz(1999, 12, 31, 24, 0, 0); -- 2.34.1
From 715d59ca850c08d06831ebb1a0df23b5e3d745ac Mon Sep 17 00:00:00 2001 From: Joseph Koshakow <kosh...@gmail.com> Date: Sat, 6 Jul 2024 15:41:09 -0400 Subject: [PATCH 3/3] Remove dependence on integer wrapping for jsonb This commit updates various jsonb operators and functions to no longer rely on integer wrapping for correctness. Not all compilers support -fwrapv, so it's best not to rely on it. --- src/backend/utils/adt/jsonfuncs.c | 4 ++-- src/test/regress/expected/jsonb.out | 12 ++++++++++++ src/test/regress/sql/jsonb.sql | 2 ++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 48c3f88140..8783c57303 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -946,7 +946,7 @@ jsonb_array_element(PG_FUNCTION_ARGS) { uint32 nelements = JB_ROOT_COUNT(jb); - if (-element > nelements) + if (element == PG_INT32_MIN || -element > nelements) PG_RETURN_NULL(); else element += nelements; @@ -5425,7 +5425,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (idx < 0) { - if (-idx > nelems) + if (idx == INT_MIN || -idx > nelems) { /* * If asked to keep elements position consistent, it's not allowed diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index e66d760189..a9d93052fc 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -680,6 +680,18 @@ select '"foo"'::jsonb -> 'z'; (1 row) +select '[]'::jsonb -> -2147483648; + ?column? +---------- + +(1 row) + +select jsonb_delete_path('{"a":[]}', '{"a",-2147483648}'); + jsonb_delete_path +------------------- + {"a": []} +(1 row) + select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> null::text; ?column? ---------- diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index 97bc2242a1..6a18577ead 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -204,6 +204,8 @@ select '[{"b": "c"}, {"b": "cc"}]'::jsonb -> 'z'; select '{"a": "c", "b": null}'::jsonb -> 'b'; select '"foo"'::jsonb -> 1; select '"foo"'::jsonb -> 'z'; +select '[]'::jsonb -> -2147483648; +select jsonb_delete_path('{"a":[]}', '{"a",-2147483648}'); select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> null::text; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> null::int; -- 2.34.1