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 98145fe370369246b5869f13f575dd8c83f64189..a09b7fa812cb2683b70e6520e83939952248cb45 100644 (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);
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 b6cf57578258d7924dc2eeb38fd455d4030e1c21..b39ab82d43d500b48bdc472bb05cc0c44d0a6321 100644 (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;
index dd5c668a0f49e6598ccd04e11614a884e66c6ac5..2d4ed1c9ae24f83968c1c350124752140b03e36d 100644 (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));