]> 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 2c00de5041808a498ff8c95e420ff83ca1d331bf..342f86537d1d201a99282b1ff72ffdb938e7a14d 100644 (file)
@@ -234,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));
 }
 
@@ -242,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 7ac73cb8c2d539d8cf8435d70309ca50af9be092..14ddb7161b015af63f15ae2a16d5cc982fcf05b4 100644 (file)
@@ -307,6 +307,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 0e8e0654575854e816b6c82a106d104af708f445..7a2b755722dd562047d0ccfb2deb5f1bc467ef44 100644 (file)
@@ -444,11 +444,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 e9f108425c5a8cab5ccd9ab24bcd6044d591f64b..a18a0b93d0a0b56592a9d18ab5ab7bcdabe9d722 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
  */
@@ -181,10 +205,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++)
@@ -251,6 +279,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 fd94e0c8818292ce7094cef4800c83bfa1703378..dbab114a6e15285b837f6c8f78c85ea32ed27699 100644 (file)
@@ -187,6 +187,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
  */
@@ -232,10 +256,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++)
@@ -304,6 +332,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 4329925f1e3e5c6fd8b4488d672ab5d5de10fb36..43446293726a0e330d0567973561fda77877d441 100644 (file)
@@ -57,6 +57,7 @@ extern uint64 pg_strtouint64(const char *str, char **endptr, int base);
 
 /* 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 2a995043bab5828735b37ba1311a18067c1412a0..112edcb64f64b57b8bcf4bf42351a75bbe4f4036 100644 (file)
@@ -1543,6 +1543,11 @@ select '[0:1]={1.1,2.2}'::float8[];
 (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 0b9bdc7a628508029a4ebd7729c1438b16584767..da7afb62b59577c62c657719f1f77950bded86b5 100644 (file)
@@ -474,6 +474,10 @@ select array[]::text[];
 select '[0:1]={1.1,2.2}'::float8[];
 -- 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[]);