Guard against unexpected dimensions of oidvector/int2vector.

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
This commit is contained in:
Tom Lane
2026-02-09 09:57:43 -05:00
parent d536aee556
commit 60e7ae41a6
8 changed files with 82 additions and 3 deletions

View 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));

View 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);

View 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';

View 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);
}

View 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);
}

View 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);

View 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

View 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[]);