Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
09f4ce5
Harden our regex engine against integer overflow in size calculations.
tglsfdc May 11, 2026
7be7622
Prevent buffer overrun in unicode_normalize().
tglsfdc May 11, 2026
58e9964
Fix assorted places that need to use palloc_array().
tglsfdc May 11, 2026
9f1b724
Add raw_connect and raw_connect_works to Cluster.pm
michaelpq May 11, 2026
6c6e94b
Fix unbounded recursive handling of SSL/GSS in ProcessStartupPacket()
michaelpq May 11, 2026
196b0cf
Unify src/common/'s definitions of MaxAllocSize.
tglsfdc May 11, 2026
49e1cb0
Guard against overflow in "left" fields of query_int and ltxtquery.
tglsfdc May 11, 2026
31b26c5
Apply timingsafe_bcmp() in authentication paths
michaelpq May 11, 2026
674acc6
Avoid passing unintended format codes to snprintf().
tglsfdc May 11, 2026
f250df8
Guard against unsafe conditions in usage of pg_strftime().
tglsfdc May 11, 2026
2ae7f79
Check CREATE privilege on multirange type schema in CREATE TYPE.
nathan-bossart May 11, 2026
0f42d5f
Avoid overflow in size calculations in formatting.c.
nathan-bossart May 11, 2026
b25ad7f
Prevent path traversal in pg_basebackup and pg_rewind
michaelpq May 11, 2026
ca6a320
Fix integer-overflow and alignment hazards in locale-related code.
tglsfdc May 11, 2026
a896a8a
Fix integer overflow in array_agg(), when the array grows too large
hlinnaka May 11, 2026
c2486f6
Mark PQfn() unsafe and fix overrun in frontend LO interface.
nathan-bossart May 11, 2026
6aa283d
refint: Fix SQL injection and buffer overruns.
nathan-bossart May 11, 2026
2d834fb
Merge branch 'main' into cp_cve
reshke May 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 48 additions & 12 deletions contrib/intarray/_int_bool.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,35 +436,66 @@ boolop(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}

/*
* Recursively fill the "left" fields of an ITEM array that represents
* a valid postfix tree.
*
* ptr: starting element of array
* pos: in/out argument, the array index this call is responsible to fill
*
* At exit, *pos has been decremented to point before the sub-tree whose
* top is the entry-time value of *pos.
*/
static void
findoprnd(ITEM *ptr, int32 *pos)
{
int32 mypos;

/* since this function recurses, it could be driven to stack overflow. */
check_stack_depth();

/* get the position this call is supposed to update */
mypos = *pos;
Assert(mypos >= 0);

/* in all cases, we should decrement *pos to advance over this item */
(*pos)--;

#ifdef BS_DEBUG
elog(DEBUG3, (ptr[*pos].type == OPR) ?
"%d %c" : "%d %d", *pos, ptr[*pos].val);
elog(DEBUG3, (ptr[mypos].type == OPR) ?
"%d %c" : "%d %d", mypos, ptr[mypos].val);
#endif
if (ptr[*pos].type == VAL)

if (ptr[mypos].type == VAL)
{
ptr[*pos].left = 0;
(*pos)--;
/* base case: a VAL has no operand, so just set its left to zero */
ptr[mypos].left = 0;
}
else if (ptr[*pos].val == (int32) '!')
else if (ptr[mypos].val == (int32) '!')
{
ptr[*pos].left = -1;
(*pos)--;
/* unary operator, likewise easy: operand is just before it */
ptr[mypos].left = -1;
/* recurse to scan operand */
findoprnd(ptr, pos);
}
else
{
ITEM *curitem = &ptr[*pos];
int32 tmp = *pos;
/* binary operator */
int32 delta;

(*pos)--;
/* recurse to scan right operand */
findoprnd(ptr, pos);
curitem->left = *pos - tmp;
/* we must fill left with offset to left operand's top */
/* abs(delta) < QUERYTYPEMAXITEMS, so it can't overflow ... */
delta = *pos - mypos;
/* ... but it might be too large to fit in the 16-bit left field */
Assert(delta < 0);
if (unlikely(delta < PG_INT16_MIN))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("query_int expression is too complex")));
ptr[mypos].left = (int16) delta;
/* recurse to scan left operand */
findoprnd(ptr, pos);
}
}
Expand Down Expand Up @@ -514,6 +545,7 @@ bqarr_in(PG_FUNCTION_ARGS)
query->size = state.num;
ptr = GETQUERY(query);

/* fill the query array from the data makepol constructed */
for (i = state.num - 1; i >= 0; i--)
{
ptr[i].type = state.str->type;
Expand All @@ -523,8 +555,12 @@ bqarr_in(PG_FUNCTION_ARGS)
state.str = tmp;
}

/* now fill the "left" fields */
pos = query->size - 1;
findoprnd(ptr, &pos);
/* if successful, findoprnd should have scanned the whole array */
Assert(pos == -1);

#ifdef BS_DEBUG
initStringInfo(&pbuf);
for (i = 0; i < query->size; i++)
Expand Down
3 changes: 3 additions & 0 deletions contrib/intarray/expected/_int.out
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,9 @@ SELECT '1&(2&(4&(5|!6)))'::query_int;
1 & 2 & 4 & ( 5 | !6 )
(1 row)

SELECT (SELECT '0 | ' || string_agg(i::text, ' & ')
FROM generate_series(1, 17000) AS i)::query_int;
ERROR: query_int expression is too complex
CREATE TABLE test__int( a int[] );
NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table.
HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew.
Expand Down
2 changes: 2 additions & 0 deletions contrib/intarray/sql/_int.sql
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ SELECT '1&(2&(4&(5&6)))'::query_int;
SELECT '1&2&4&5&6'::query_int;
SELECT '1&(2&(4&(5|6)))'::query_int;
SELECT '1&(2&(4&(5|!6)))'::query_int;
SELECT (SELECT '0 | ' || string_agg(i::text, ' & ')
FROM generate_series(1, 17000) AS i)::query_int;


CREATE TABLE test__int( a int[] );
Expand Down
3 changes: 3 additions & 0 deletions contrib/ltree/expected/ltree.out
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,9 @@ SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & aw_rw%*'::ltxtquery;
f
(1 row)

SELECT (SELECT 'a | ' || string_agg('b', ' & ')
FROM generate_series(1, 17000) AS i)::ltxtquery;
ERROR: ltxtquery is too large
--arrays
SELECT '{1.2.3}'::ltree[] @> '1.2.3.4';
?column?
Expand Down
51 changes: 41 additions & 10 deletions contrib/ltree/ltxtquery_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,31 +271,60 @@ makepol(QPRS_STATE *state)
return END;
}

/*
* Recursively fill the "left" fields of an ITEM array that represents
* a valid postfix tree.
*
* ptr: starting element of array
* pos: in/out argument, the array index this call is responsible to fill
*
* At exit, *pos has been incremented to point after the sub-tree whose
* top is the entry-time value of *pos.
*/
static void
findoprnd(ITEM *ptr, int32 *pos)
{
int32 mypos;

/* since this function recurses, it could be driven to stack overflow. */
check_stack_depth();

if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
/* get the position this call is supposed to update */
mypos = *pos;

/* in all cases, we should increment *pos to advance over this item */
(*pos)++;

if (ptr[mypos].type == VAL || ptr[mypos].type == VALTRUE)
{
ptr[*pos].left = 0;
(*pos)++;
/* base case: a VAL has no operand, so just set its left to zero */
ptr[mypos].left = 0;
}
else if (ptr[*pos].val == (int32) '!')
else if (ptr[mypos].val == (int32) '!')
{
ptr[*pos].left = 1;
(*pos)++;
/* unary operator, likewise easy: operand is just after it */
ptr[mypos].left = 1;
/* recurse to scan operand */
findoprnd(ptr, pos);
}
else
{
ITEM *curitem = &ptr[*pos];
int32 tmp = *pos;
/* binary operator */
int32 delta;

(*pos)++;
/* recurse to scan right operand */
findoprnd(ptr, pos);
curitem->left = *pos - tmp;
/* we must fill left with offset to left operand's top */
/* delta can't overflow, see LTXTQUERY_TOO_BIG ... */
delta = *pos - mypos;
/* ... but it might be too large to fit in the 16-bit left field */
Assert(delta > 0);
if (unlikely(delta > PG_INT16_MAX))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("ltxtquery is too large")));
ptr[mypos].left = (int16) delta;
/* recurse to scan left operand */
findoprnd(ptr, pos);
}
}
Expand Down Expand Up @@ -372,6 +401,8 @@ queryin(char *buf)
/* set left operand's position for every operator */
pos = 0;
findoprnd(ptr, &pos);
/* if successful, findoprnd should have scanned the whole array */
Assert(pos == state.num);

return query;
}
Expand Down
3 changes: 3 additions & 0 deletions contrib/ltree/sql/ltree.sql
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ SELECT 'tree.awdfg'::ltree @ 'tree & aWdfg@'::ltxtquery;
SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & aw_qw%*'::ltxtquery;
SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & aw_rw%*'::ltxtquery;

SELECT (SELECT 'a | ' || string_agg('b', ' & ')
FROM generate_series(1, 17000) AS i)::ltxtquery;

--arrays

SELECT '{1.2.3}'::ltree[] @> '1.2.3.4';
Expand Down
84 changes: 38 additions & 46 deletions contrib/spi/refint.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,21 +166,24 @@ check_primary_key(PG_FUNCTION_ARGS)
if (plan->nplans <= 0)
{
SPIPlanPtr pplan;
char sql[8192];
StringInfoData sql;

initStringInfo(&sql);

/*
* Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 =
* $1 [AND Pkey2 = $2 [...]]
*/
snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
for (i = 0; i < nkeys; i++)
appendStringInfo(&sql, "select 1 from %s where ", relname);
for (i = 1; i <= nkeys; i++)
{
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
appendStringInfo(&sql, "%s = $%d ", args[i + nkeys], i);
if (i < nkeys)
appendStringInfoString(&sql, "and ");
}

/* Prepare plan for query */
pplan = SPI_prepare(sql, nkeys, argtypes);
pplan = SPI_prepare(sql.data, nkeys, argtypes);
if (pplan == NULL)
/* internal error */
elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
Expand All @@ -196,6 +199,8 @@ check_primary_key(PG_FUNCTION_ARGS)
sizeof(SPIPlanPtr));
*(plan->splan) = pplan;
plan->nplans = 1;

pfree(sql.data);
}

/*
Expand Down Expand Up @@ -416,14 +421,17 @@ check_foreign_key(PG_FUNCTION_ARGS)
if (plan->nplans <= 0)
{
SPIPlanPtr pplan;
char sql[8192];
char **args2 = args;

plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
nrefs * sizeof(SPIPlanPtr));

for (r = 0; r < nrefs; r++)
{
StringInfoData sql;

initStringInfo(&sql);

relname = args2[0];

/*---------
Expand All @@ -437,8 +445,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
*---------
*/
if (action == 'r')

snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
appendStringInfo(&sql, "select 1 from %s where ", relname);

/*---------
* For 'C'ascade action we construct DELETE query
Expand All @@ -465,43 +472,24 @@ check_foreign_key(PG_FUNCTION_ARGS)
char *nv;
int k;

snprintf(sql, sizeof(sql), "update %s set ", relname);
appendStringInfo(&sql, "update %s set ", relname);
for (k = 1; k <= nkeys; k++)
{
int is_char_type = 0;
char *type;

fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
Assert(fn > 0); /* already checked above */
nv = SPI_getvalue(newtuple, tupdesc, fn);
type = SPI_gettype(tupdesc, fn);

if (strcmp(type, "text") == 0 ||
strcmp(type, "varchar") == 0 ||
strcmp(type, "char") == 0 ||
strcmp(type, "bpchar") == 0 ||
strcmp(type, "date") == 0 ||
strcmp(type, "timestamp") == 0)
is_char_type = 1;
#ifdef DEBUG_QUERY
elog(DEBUG4, "check_foreign_key Debug value %s type %s %d",
nv, type, is_char_type);
#endif

/*
* is_char_type =1 i set ' ' for define a new value
*/
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
" %s = %s%s%s %s ",
args2[k], (is_char_type > 0) ? "'" : "",
nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
appendStringInfo(&sql, " %s = %s ",
args2[k], quote_literal_cstr(nv));
if (k < nkeys)
appendStringInfoString(&sql, ", ");
}
strcat(sql, " where ");
appendStringInfoString(&sql, " where ");

}
else
/* DELETE */
snprintf(sql, sizeof(sql), "delete from %s where ", relname);
appendStringInfo(&sql, "delete from %s where ", relname);

}

Expand All @@ -513,25 +501,26 @@ check_foreign_key(PG_FUNCTION_ARGS)
*/
else if (action == 's')
{
snprintf(sql, sizeof(sql), "update %s set ", relname);
appendStringInfo(&sql, "update %s set ", relname);
for (i = 1; i <= nkeys; i++)
{
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
"%s = null%s",
args2[i], (i < nkeys) ? ", " : "");
appendStringInfo(&sql, "%s = null", args2[i]);
if (i < nkeys)
appendStringInfoString(&sql, ", ");
}
strcat(sql, " where ");
appendStringInfoString(&sql, " where ");
}

/* Construct WHERE qual */
for (i = 1; i <= nkeys; i++)
{
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
args2[i], i, (i < nkeys) ? "and " : "");
appendStringInfo(&sql, "%s = $%d ", args2[i], i);
if (i < nkeys)
appendStringInfoString(&sql, "and ");
}

/* Prepare plan for query */
pplan = SPI_prepare(sql, nkeys, argtypes);
pplan = SPI_prepare(sql.data, nkeys, argtypes);
if (pplan == NULL)
/* internal error */
elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
Expand All @@ -547,11 +536,14 @@ check_foreign_key(PG_FUNCTION_ARGS)
plan->splan[r] = pplan;

args2 += nkeys + 1; /* to the next relation */

#ifdef DEBUG_QUERY
elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql.data);
#endif

pfree(sql.data);
}
plan->nplans = nrefs;
#ifdef DEBUG_QUERY
elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql);
#endif
}

/*
Expand Down
Loading
Loading