]> 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 f890f79ee180a8dd931d3974a0e99d611db80271..e2455e893755bb2acd712f234dd0cd2be0e47634 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 7e18e2fc62fc4d90ecee26837def03b155d1ddf0..5b080fa89b51846f61e2f3ab749c53ea4a273f04 100644 (file)
@@ -299,6 +299,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 2918fdbfb6505f68d6afef171aa4899ae7c613a5..9d6300d1efc2e4cf4433236b422bf86aee72005c 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 ff1f46e2b42d2cb814b28edaadcc3486b73911df..41d9cbcdd6007fb9d931380c7cc3e9fc74e1fdb9 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
  */
@@ -207,10 +231,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++)
@@ -271,6 +299,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 7de31d73d33eed1a7d92bac5a56befd836851eb7..a0989455efbe566b6bfa7683e6db6cc99da83e0a 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
  */
@@ -235,10 +259,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++)
@@ -301,6 +329,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 221c3e6c3dea67151da3b000a2080ffeb3cdba5b..ec45fcaad02bcf477c70cd5f2717ff3d129ccabd 100644 (file)
@@ -56,6 +56,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 281aa3769c4df6970d2846e8e53493e78333a57a..695481fceef3c3304cc1781a98f8c27302b8f6e2 100644 (file)
@@ -1556,6 +1556,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 dd15d6174efc862ba3c1215488f7791c933af75d..c822133c1d89ab65f792a05630217a35d58021e7 100644 (file)
@@ -491,6 +491,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[]);