mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-22 06:17:00 +08:00
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:
@ -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));
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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[]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user