From PostgreSQL 14.23 Resolves: RHEL-179806 Co-authored-by: Cursor <cursoragent@cursor.com>
204 lines
6.1 KiB
Diff
204 lines
6.1 KiB
Diff
From 2b026df29c1e1caafb259a2021528a28ec484018 Mon Sep 17 00:00:00 2001
|
|
From: Nathan Bossart <nathan@postgresql.org>
|
|
Date: Mon, 11 May 2026 05:13:52 -0700
|
|
Subject: [PATCH] refint: Fix SQL injection and buffer overruns.
|
|
|
|
Maliciously crafted key value updates could achieve SQL injection
|
|
within check_foreign_key(). To fix, ensure new key values are
|
|
properly quoted and escaped in the internally generated SQL
|
|
statements. While at it, avoid potential buffer overruns by
|
|
replacing the stack buffers for internally generated SQL statements
|
|
with StringInfo.
|
|
|
|
Reported-by: Nikolay Samokhvalov <nik@postgres.ai>
|
|
Author: Nathan Bossart <nathandbossart@gmail.com>
|
|
Reviewed-by: Noah Misch <noah@leadboat.com>
|
|
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
|
|
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
|
|
Security: CVE-2026-6637
|
|
Backpatch-through: 14
|
|
---
|
|
contrib/spi/refint.c | 84 ++++++++++++++++++++------------------------
|
|
1 file changed, 38 insertions(+), 46 deletions(-)
|
|
|
|
diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
|
|
index 6fbfef2..cbef463 100644
|
|
--- a/contrib/spi/refint.c
|
|
+++ b/contrib/spi/refint.c
|
|
@@ -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));
|
|
@@ -196,6 +199,8 @@ check_primary_key(PG_FUNCTION_ARGS)
|
|
sizeof(SPIPlanPtr));
|
|
*(plan->splan) = pplan;
|
|
plan->nplans = 1;
|
|
+
|
|
+ pfree(sql.data);
|
|
}
|
|
|
|
/*
|
|
@@ -416,7 +421,6 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
|
if (plan->nplans <= 0)
|
|
{
|
|
SPIPlanPtr pplan;
|
|
- char sql[8192];
|
|
char **args2 = args;
|
|
|
|
plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
|
|
@@ -424,6 +428,10 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
|
|
|
for (r = 0; r < nrefs; r++)
|
|
{
|
|
+ StringInfoData sql;
|
|
+
|
|
+ initStringInfo(&sql);
|
|
+
|
|
relname = args2[0];
|
|
|
|
/*---------
|
|
@@ -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
|
|
@@ -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);
|
|
|
|
}
|
|
|
|
@@ -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));
|
|
@@ -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
|
|
}
|
|
|
|
/*
|
|
--
|
|
2.39.5 (Apple Git-154)
|
|
|