]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Guard against unexpected dimensions of oidvector/int2vector.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 9 Feb 2026 14:57:44 +0000 (09:57 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 9 Feb 2026 14:57:44 +0000 (09:57 -0500)
These data types are represented like full-fledged arrays, but
functions that deal specifically with these types assume that the
array is 1-dimensional and contains no nulls.  However, there are
cast pathways that allow general oid[] or int2[] arrays to be cast
to these types, allowing these expectations to be violated.  This
can be exploited to cause server memory disclosure or SIGSEGV.
Fix by installing explicit checks in functions that accept these
types.

Reported-by: Altan Birler <altan.birler@tum.de>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Noah Misch <noah@leadboat.com>
Security: CVE-2026-2003
Backpatch-through: 14

src/backend/access/hash/hashfunc.c
src/backend/access/nbtree/nbtcompare.c
src/backend/utils/adt/format_type.c
src/backend/utils/adt/int.c
src/backend/utils/adt/oid.c
src/include/utils/builtins.h
src/test/regress/expected/arrays.out
src/test/regress/sql/arrays.sql

index c3a67b51afe0b9e49b5c077d701506c4b1c6e6ba..e08656caa4f1fd0d7930ddf4513c5b5afbb3abaa 100644 (file)
@@ -27,6 +27,7 @@
 #include "postgres.h"
 
 #include "common/hashfn.h"
+#include "utils/builtins.h"
 #include "utils/float.h"
 #include "utils/fmgrprotos.h"
 #include "utils/pg_locale.h"
@@ -233,6 +234,7 @@ hashoidvector(PG_FUNCTION_ARGS)
 {
        oidvector  *key = (oidvector *) PG_GETARG_POINTER(0);
 
+       check_valid_oidvector(key);
        return hash_any((unsigned char *) key->values, key->dim1 * sizeof(Oid));
 }
 
@@ -241,6 +243,7 @@ hashoidvectorextended(PG_FUNCTION_ARGS)
 {
        oidvector  *key = (oidvector *) PG_GETARG_POINTER(0);
 
+       check_valid_oidvector(key);
        return hash_any_extended((unsigned char *) key->values,
                                                         key->dim1 * sizeof(Oid),
                                                         PG_GETARG_INT64(1));
index 1c72867c84e5ee3743cd78c3c1cffb074c6e18d7..1a6328ba1ff549b081be6dee758c568dff308b74 100644 (file)
@@ -57,6 +57,7 @@
 
 #include <limits.h>
 
+#include "utils/builtins.h"
 #include "utils/fmgrprotos.h"
 #include "utils/sortsupport.h"
 
@@ -299,6 +300,9 @@ btoidvectorcmp(PG_FUNCTION_ARGS)
        oidvector  *b = (oidvector *) PG_GETARG_POINTER(1);
        int                     i;
 
+       check_valid_oidvector(a);
+       check_valid_oidvector(b);
+
        /* We arbitrarily choose to sort first by vector length */
        if (a->dim1 != b->dim1)
                PG_RETURN_INT32(a->dim1 - b->dim1);
index 28ba0fbd1929adda262fa096e2262a60e8e63119..175cee614f66592c3552e7eb1b4b2e689b3749aa 100644 (file)
@@ -448,11 +448,15 @@ oidvectortypes(PG_FUNCTION_ARGS)
 {
        oidvector  *oidArray = (oidvector *) PG_GETARG_POINTER(0);
        char       *result;
-       int                     numargs = oidArray->dim1;
+       int                     numargs;
        int                     num;
        size_t          total;
        size_t          left;
 
+       /* validate input before fetching dim1 */
+       check_valid_oidvector(oidArray);
+       numargs = oidArray->dim1;
+
        total = 20 * numargs + 1;
        result = palloc(total);
        result[0] = '\0';
index 234f20796b761cb1d42688ff473db8a1215c0889..df07ac0037bc82a4fae5c6976335ffdf192f2d7c 100644 (file)
@@ -134,6 +134,30 @@ buildint2vector(const int16 *int2s, int n)
        return result;
 }
 
+/*
+ * validate that an array object meets the restrictions of int2vector
+ *
+ * We need this because there are pathways by which a general int2[] array can
+ * be cast to int2vector, allowing the type's restrictions to be violated.
+ * All code that receives an int2vector as a SQL parameter should check this.
+ */
+static void
+check_valid_int2vector(const int2vector *int2Array)
+{
+       /*
+        * We insist on ndim == 1 and dataoffset == 0 (that is, no nulls) because
+        * otherwise the array's layout will not be what calling code expects.  We
+        * needn't be picky about the index lower bound though.  Checking elemtype
+        * is just paranoia.
+        */
+       if (int2Array->ndim != 1 ||
+               int2Array->dataoffset != 0 ||
+               int2Array->elemtype != INT2OID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("array is not a valid int2vector")));
+}
+
 /*
  *             int2vectorin                    - converts "num num ..." to internal form
  */
@@ -208,10 +232,14 @@ int2vectorout(PG_FUNCTION_ARGS)
 {
        int2vector *int2Array = (int2vector *) PG_GETARG_POINTER(0);
        int                     num,
-                               nnums = int2Array->dim1;
+                               nnums;
        char       *rp;
        char       *result;
 
+       /* validate input before fetching dim1 */
+       check_valid_int2vector(int2Array);
+       nnums = int2Array->dim1;
+
        /* assumes sign, 5 digits, ' ' */
        rp = result = (char *) palloc(nnums * 7 + 1);
        for (num = 0; num < nnums; num++)
@@ -272,6 +300,7 @@ int2vectorrecv(PG_FUNCTION_ARGS)
 Datum
 int2vectorsend(PG_FUNCTION_ARGS)
 {
+       /* We don't do check_valid_int2vector, since array_send won't care */
        return array_send(fcinfo);
 }
 
index 56fb1fd77cee5f89d389723a948efa33fe3fad2b..1082817039707c121a515743882062401b3b8684 100644 (file)
@@ -107,6 +107,30 @@ buildoidvector(const Oid *oids, int n)
        return result;
 }
 
+/*
+ * validate that an array object meets the restrictions of oidvector
+ *
+ * We need this because there are pathways by which a general oid[] array can
+ * be cast to oidvector, allowing the type's restrictions to be violated.
+ * All code that receives an oidvector as a SQL parameter should check this.
+ */
+void
+check_valid_oidvector(const oidvector *oidArray)
+{
+       /*
+        * We insist on ndim == 1 and dataoffset == 0 (that is, no nulls) because
+        * otherwise the array's layout will not be what calling code expects.  We
+        * needn't be picky about the index lower bound though.  Checking elemtype
+        * is just paranoia.
+        */
+       if (oidArray->ndim != 1 ||
+               oidArray->dataoffset != 0 ||
+               oidArray->elemtype != OIDOID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("array is not a valid oidvector")));
+}
+
 /*
  *             oidvectorin                     - converts "num num ..." to internal form
  */
@@ -159,10 +183,14 @@ oidvectorout(PG_FUNCTION_ARGS)
 {
        oidvector  *oidArray = (oidvector *) PG_GETARG_POINTER(0);
        int                     num,
-                               nnums = oidArray->dim1;
+                               nnums;
        char       *rp;
        char       *result;
 
+       /* validate input before fetching dim1 */
+       check_valid_oidvector(oidArray);
+       nnums = oidArray->dim1;
+
        /* assumes sign, 10 digits, ' ' */
        rp = result = (char *) palloc(nnums * 12 + 1);
        for (num = 0; num < nnums; num++)
@@ -225,6 +253,7 @@ oidvectorrecv(PG_FUNCTION_ARGS)
 Datum
 oidvectorsend(PG_FUNCTION_ARGS)
 {
+       /* We don't do check_valid_oidvector, since array_send won't care */
        return array_send(fcinfo);
 }
 
index 359c570f23e20e36cfcc461f2fa88b26f21b656d..26cf7a70842e9bda3cf4c8b3e6e36aa799f24eea 100644 (file)
@@ -68,6 +68,7 @@ extern char *pg_ultostr(char *str, uint32 value);
 
 /* oid.c */
 extern oidvector *buildoidvector(const Oid *oids, int n);
+extern void check_valid_oidvector(const oidvector *oidArray);
 extern Oid     oidparse(Node *node);
 extern int     oid_cmp(const void *p1, const void *p2);
 
index 44fa7b214c71ad46fb65871c205210cd914808d1..edf90231bbe960d0ddb593bdfff1933b26e403b3 100644 (file)
@@ -1737,6 +1737,11 @@ select '[-2147483648:-2147483647]={1,2}'::int[];
 (1 row)
 
 -- all of the above should be accepted
+-- some day we might allow these cases, but for now they're errors:
+select array[]::oidvector;
+ERROR:  array is not a valid oidvector
+select array[]::int2vector;
+ERROR:  array is not a valid int2vector
 -- tests for array aggregates
 CREATE TEMP TABLE arraggtest ( f1 INT[], f2 TEXT[][], f3 FLOAT[]);
 INSERT INTO arraggtest (f1, f2, f3) VALUES
index 305371debae3b945f9cd17dfd9745c97b2f2bb82..339eee7922439d1e53be4dbf24cc733c29416cfa 100644 (file)
@@ -528,6 +528,10 @@ select '[2147483646:2147483646]={1}'::int[];
 select '[-2147483648:-2147483647]={1,2}'::int[];
 -- all of the above should be accepted
 
+-- some day we might allow these cases, but for now they're errors:
+select array[]::oidvector;
+select array[]::int2vector;
+
 -- tests for array aggregates
 CREATE TEMP TABLE arraggtest ( f1 INT[], f2 TEXT[][], f3 FLOAT[]);