]> 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:43 +0000 (09:57 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 9 Feb 2026 14:57:43 +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 83bda209c4255a01ae4e2427e4edb0ac42875bc6..036421fc664f31637aefe925ea29fd40def7016e 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 8425805a292956751a712fa0eb3a2cc031280192..1d343377e9801a791ca14c277e51a89c3d6e78d4 100644 (file)
@@ -57,6 +57,7 @@
 
 #include <limits.h>
 
+#include "utils/builtins.h"
 #include "utils/fmgrprotos.h"
 #include "utils/skipsupport.h"
 #include "utils/sortsupport.h"
@@ -587,6 +588,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 544205ca067317526d1cebf91a83faf2fa0543a9..3cd5053d11802512cf4d7f47ab25d0833b00005d 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 d230262658590affe94f37367b4d601638ce9781..ff54d50ea9ddfb84097a33a435b9c792f0c19c97 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 6f4c299dee9b187de271d696817be42a2083971e..a3419728971dcd1e023a3c49c1d1ffbc8d9b9018 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 cf57819ebdc84dd6ddad1502e3f2427f7090fb71..5dcd788ff8080c0336dd3a76faa8e1dcd515c047 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 e1ab6dc278a5f898f3f9e282f71d7e456fec24b9..66439d427a317f74c61d10a4dc9d281d5afb4876 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 450389831a046339d43709525302705284c82518..82837af7c4a578719e5028de21da7cff7d347532 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[]);