Fix integer-overflow problem in intarray's g_int_decompress().
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 7 Jan 2024 20:19:50 +0000 (15:19 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 7 Jan 2024 20:19:50 +0000 (15:19 -0500)
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

contrib/intarray/_int_gist.c
contrib/intarray/data/test__int.data
contrib/intarray/expected/_int.out
contrib/intarray/sql/_int.sql

index ea79c4bb51f979a93e102a4f40fa16d63d05858c..5d46b6bc13ec9aa945fce7283bdc459b214dfa9f 100644 (file)
@@ -296,8 +296,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
    ArrayType  *in;
    int         lenin;
    int        *din;
-   int         i,
-               j;
+   int         i;
 
    in = DatumGetArrayTypeP(entry->key);
 
@@ -341,9 +340,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);
index b3903d0f33a60d2bf1a0b5ccbc797a2ddcae9d6b..0a7fac3c08722d6229040860c2ca18f488d35fe0 100644 (file)
 {173,208,229}
 {6,22,142,267,299}
 {22,122,173,245,293}
+{1,2,101,102,201,202,2147483647}
index 2f33c7e18d52780b839070e4cab110bb96540022..09ab23483f7bd81df96306a8e9710b4d6b0f9ef9 100644 (file)
@@ -464,13 +464,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
@@ -538,13 +538,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
@@ -620,13 +620,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;
@@ -700,13 +700,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;
@@ -774,13 +774,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;
@@ -848,13 +848,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;
@@ -870,9 +870,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));
@@ -939,13 +940,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;
index bd3e01208d5ecb19d1ff720d0cc6c75d015190ba..95eec96c14e811ef25e81a1fb65aaa9fdef46381 100644 (file)
@@ -194,9 +194,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));