Fix integer-overflow problem in intarray's g_int_decompress().

An array element equal to INT_MAX gave this code indigestion,
causing an infinite loop that surely ended in SIGSEGV.  We fixed
some nearby problems awhile ago (cf 757c5182f) but missed this.

Report and diagnosis by Alexander Lakhin (bug #18273); patch by me

Discussion: https://postgr.es/m/18273-9a832d1da122600c@postgresql.org
This commit is contained in:
Tom Lane 2024-01-07 15:19:50 -05:00
parent 714a987bc1
commit cf6f802bf5
4 changed files with 27 additions and 22 deletions

View File

@ -297,8 +297,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
ArrayType *in;
int lenin;
int *din;
int i,
j;
int i;
in = DatumGetArrayTypeP(entry->key);
@ -342,9 +341,12 @@ g_int_decompress(PG_FUNCTION_ARGS)
dr = ARRPTR(r);
for (i = 0; i < lenin; i += 2)
for (j = din[i]; j <= din[i + 1]; j++)
{
/* use int64 for j in case din[i + 1] is INT_MAX */
for (int64 j = din[i]; j <= din[i + 1]; j++)
if ((!i) || *(dr - 1) != j)
*dr++ = j;
*dr++ = (int) j;
}
if (in != (ArrayType *) DatumGetPointer(entry->key))
pfree(in);

View File

@ -6998,3 +6998,4 @@
{173,208,229}
{6,22,142,267,299}
{22,122,173,245,293}
{1,2,101,102,201,202,2147483647}

View File

@ -483,13 +483,13 @@ SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
SELECT count(*) from test__int WHERE a @@ '20 | !21';
count
-------
6566
6567
(1 row)
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
count
-------
6343
6344
(1 row)
SET enable_seqscan = off; -- not all of these would use index by default
@ -557,13 +557,13 @@ SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
SELECT count(*) from test__int WHERE a @@ '20 | !21';
count
-------
6566
6567
(1 row)
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
count
-------
6343
6344
(1 row)
INSERT INTO test__int SELECT array(SELECT x FROM generate_series(1, 1001) x); -- should fail
@ -639,13 +639,13 @@ SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
SELECT count(*) from test__int WHERE a @@ '20 | !21';
count
-------
6566
6567
(1 row)
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
count
-------
6343
6344
(1 row)
DROP INDEX text_idx;
@ -719,13 +719,13 @@ SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
SELECT count(*) from test__int WHERE a @@ '20 | !21';
count
-------
6566
6567
(1 row)
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
count
-------
6343
6344
(1 row)
DROP INDEX text_idx;
@ -793,13 +793,13 @@ SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
SELECT count(*) from test__int WHERE a @@ '20 | !21';
count
-------
6566
6567
(1 row)
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
count
-------
6343
6344
(1 row)
DROP INDEX text_idx;
@ -867,13 +867,13 @@ SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
SELECT count(*) from test__int WHERE a @@ '20 | !21';
count
-------
6566
6567
(1 row)
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
count
-------
6343
6344
(1 row)
DROP INDEX text_idx;
@ -889,9 +889,10 @@ DROP INDEX text_idx;
-- core that would reach the same codepaths.
CREATE TABLE more__int AS SELECT
-- Leave alone NULLs, empty arrays and the one row that we use to test
-- equality
-- equality; also skip INT_MAX
CASE WHEN a IS NULL OR a = '{}' OR a = '{73,23,20}' THEN a ELSE
(select array_agg(u) || array_agg(u + 1000) || array_agg(u + 2000) from (select unnest(a) u) x)
(select array_agg(u) || array_agg(u + 1000) || array_agg(u + 2000)
from unnest(a) u where u < 2000000000)
END AS a, a as b
FROM test__int;
CREATE INDEX ON more__int using gist (a gist__int_ops(numranges = 252));
@ -958,13 +959,13 @@ SELECT count(*) from more__int WHERE a @@ '(20&23)|(50&68)';
SELECT count(*) from more__int WHERE a @@ '20 | !21';
count
-------
6566
6567
(1 row)
SELECT count(*) from more__int WHERE a @@ '!20 & !21';
count
-------
6343
6344
(1 row)
RESET enable_seqscan;

View File

@ -209,9 +209,10 @@ DROP INDEX text_idx;
-- core that would reach the same codepaths.
CREATE TABLE more__int AS SELECT
-- Leave alone NULLs, empty arrays and the one row that we use to test
-- equality
-- equality; also skip INT_MAX
CASE WHEN a IS NULL OR a = '{}' OR a = '{73,23,20}' THEN a ELSE
(select array_agg(u) || array_agg(u + 1000) || array_agg(u + 2000) from (select unnest(a) u) x)
(select array_agg(u) || array_agg(u + 1000) || array_agg(u + 2000)
from unnest(a) u where u < 2000000000)
END AS a, a as b
FROM test__int;
CREATE INDEX ON more__int using gist (a gist__int_ops(numranges = 252));