Skip to content

Commit 6203bcb

Browse files
nathan-bossartRucha Kulkarni
authored andcommitted
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 (cherry picked from commit 710995782fa388f3878a86e1d9ed68db8abc4276)
1 parent 11d5d0b commit 6203bcb

1 file changed

Lines changed: 38 additions & 46 deletions

File tree

contrib/spi/refint.c

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -166,21 +166,24 @@ check_primary_key(PG_FUNCTION_ARGS)
166166
if (plan->nplans <= 0)
167167
{
168168
SPIPlanPtr pplan;
169-
char sql[8192];
169+
StringInfoData sql;
170+
171+
initStringInfo(&sql);
170172

171173
/*
172174
* Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 =
173175
* $1 [AND Pkey2 = $2 [...]]
174176
*/
175-
snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
176-
for (i = 0; i < nkeys; i++)
177+
appendStringInfo(&sql, "select 1 from %s where ", relname);
178+
for (i = 1; i <= nkeys; i++)
177179
{
178-
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
179-
args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
180+
appendStringInfo(&sql, "%s = $%d ", args[i + nkeys], i);
181+
if (i < nkeys)
182+
appendStringInfoString(&sql, "and ");
180183
}
181184

182185
/* Prepare plan for query */
183-
pplan = SPI_prepare(sql, nkeys, argtypes);
186+
pplan = SPI_prepare(sql.data, nkeys, argtypes);
184187
if (pplan == NULL)
185188
/* internal error */
186189
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)
196199
sizeof(SPIPlanPtr));
197200
*(plan->splan) = pplan;
198201
plan->nplans = 1;
202+
203+
pfree(sql.data);
199204
}
200205

201206
/*
@@ -416,14 +421,17 @@ check_foreign_key(PG_FUNCTION_ARGS)
416421
if (plan->nplans <= 0)
417422
{
418423
SPIPlanPtr pplan;
419-
char sql[8192];
420424
char **args2 = args;
421425

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

425429
for (r = 0; r < nrefs; r++)
426430
{
431+
StringInfoData sql;
432+
433+
initStringInfo(&sql);
434+
427435
relname = args2[0];
428436

429437
/*---------
@@ -437,8 +445,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
437445
*---------
438446
*/
439447
if (action == 'r')
440-
441-
snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
448+
appendStringInfo(&sql, "select 1 from %s where ", relname);
442449

443450
/*---------
444451
* For 'C'ascade action we construct DELETE query
@@ -465,42 +472,23 @@ check_foreign_key(PG_FUNCTION_ARGS)
465472
char *nv;
466473
int k;
467474

468-
snprintf(sql, sizeof(sql), "update %s set ", relname);
475+
appendStringInfo(&sql, "update %s set ", relname);
469476
for (k = 1; k <= nkeys; k++)
470477
{
471-
int is_char_type = 0;
472-
char *type;
473-
474478
fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
475479
Assert(fn > 0); /* already checked above */
476480
nv = SPI_getvalue(newtuple, tupdesc, fn);
477-
type = SPI_gettype(tupdesc, fn);
478-
479-
if (strcmp(type, "text") == 0 ||
480-
strcmp(type, "varchar") == 0 ||
481-
strcmp(type, "char") == 0 ||
482-
strcmp(type, "bpchar") == 0 ||
483-
strcmp(type, "date") == 0 ||
484-
strcmp(type, "timestamp") == 0)
485-
is_char_type = 1;
486-
#ifdef DEBUG_QUERY
487-
elog(DEBUG4, "check_foreign_key Debug value %s type %s %d",
488-
nv, type, is_char_type);
489-
#endif
490481

491-
/*
492-
* is_char_type =1 i set ' ' for define a new value
493-
*/
494-
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
495-
" %s = %s%s%s %s ",
496-
args2[k], (is_char_type > 0) ? "'" : "",
497-
nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
482+
appendStringInfo(&sql, " %s = %s ",
483+
args2[k], quote_literal_cstr(nv));
484+
if (k < nkeys)
485+
appendStringInfoString(&sql, ", ");
498486
}
499-
strcat(sql, " where ");
487+
appendStringInfoString(&sql, " where ");
500488
}
501489
else
502490
/* DELETE */
503-
snprintf(sql, sizeof(sql), "delete from %s where ", relname);
491+
appendStringInfo(&sql, "delete from %s where ", relname);
504492
}
505493

506494
/*
@@ -511,25 +499,26 @@ check_foreign_key(PG_FUNCTION_ARGS)
511499
*/
512500
else if (action == 's')
513501
{
514-
snprintf(sql, sizeof(sql), "update %s set ", relname);
502+
appendStringInfo(&sql, "update %s set ", relname);
515503
for (i = 1; i <= nkeys; i++)
516504
{
517-
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
518-
"%s = null%s",
519-
args2[i], (i < nkeys) ? ", " : "");
505+
appendStringInfo(&sql, "%s = null", args2[i]);
506+
if (i < nkeys)
507+
appendStringInfoString(&sql, ", ");
520508
}
521-
strcat(sql, " where ");
509+
appendStringInfoString(&sql, " where ");
522510
}
523511

524512
/* Construct WHERE qual */
525513
for (i = 1; i <= nkeys; i++)
526514
{
527-
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
528-
args2[i], i, (i < nkeys) ? "and " : "");
515+
appendStringInfo(&sql, "%s = $%d ", args2[i], i);
516+
if (i < nkeys)
517+
appendStringInfoString(&sql, "and ");
529518
}
530519

531520
/* Prepare plan for query */
532-
pplan = SPI_prepare(sql, nkeys, argtypes);
521+
pplan = SPI_prepare(sql.data, nkeys, argtypes);
533522
if (pplan == NULL)
534523
/* internal error */
535524
elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
@@ -545,11 +534,14 @@ check_foreign_key(PG_FUNCTION_ARGS)
545534
plan->splan[r] = pplan;
546535

547536
args2 += nkeys + 1; /* to the next relation */
537+
538+
#ifdef DEBUG_QUERY
539+
elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql.data);
540+
#endif
541+
542+
pfree(sql.data);
548543
}
549544
plan->nplans = nrefs;
550-
#ifdef DEBUG_QUERY
551-
elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql);
552-
#endif
553545
}
554546

555547
/*

0 commit comments

Comments
 (0)