Skip to content

Commit 61c7dca

Browse files
nathan-bossartanju15bharti
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 1ebda7da9a43d3ae3564d08612de9cb27fbaf482)
1 parent 4792641 commit 61c7dca

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
@@ -171,21 +171,24 @@ check_primary_key(PG_FUNCTION_ARGS)
171171
if (plan->nplans <= 0)
172172
{
173173
SPIPlanPtr pplan;
174-
char sql[8192];
174+
StringInfoData sql;
175+
176+
initStringInfo(&sql);
175177

176178
/*
177179
* Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 =
178180
* $1 [AND Pkey2 = $2 [...]]
179181
*/
180-
snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
181-
for (i = 0; i < nkeys; i++)
182+
appendStringInfo(&sql, "select 1 from %s where ", relname);
183+
for (i = 1; i <= nkeys; i++)
182184
{
183-
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
184-
args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
185+
appendStringInfo(&sql, "%s = $%d ", args[i + nkeys], i);
186+
if (i < nkeys)
187+
appendStringInfoString(&sql, "and ");
185188
}
186189

187190
/* Prepare plan for query */
188-
pplan = SPI_prepare(sql, nkeys, argtypes);
191+
pplan = SPI_prepare(sql.data, nkeys, argtypes);
189192
if (pplan == NULL)
190193
/* internal error */
191194
elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
@@ -201,6 +204,8 @@ check_primary_key(PG_FUNCTION_ARGS)
201204
sizeof(SPIPlanPtr));
202205
*(plan->splan) = pplan;
203206
plan->nplans = 1;
207+
208+
pfree(sql.data);
204209
}
205210

206211
/*
@@ -423,14 +428,17 @@ check_foreign_key(PG_FUNCTION_ARGS)
423428
if (plan->nplans <= 0)
424429
{
425430
SPIPlanPtr pplan;
426-
char sql[8192];
427431
char **args2 = args;
428432

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

432436
for (r = 0; r < nrefs; r++)
433437
{
438+
StringInfoData sql;
439+
440+
initStringInfo(&sql);
441+
434442
relname = args2[0];
435443

436444
/*---------
@@ -444,8 +452,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
444452
*---------
445453
*/
446454
if (action == 'r')
447-
448-
snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
455+
appendStringInfo(&sql, "select 1 from %s where ", relname);
449456

450457
/*---------
451458
* For 'C'ascade action we construct DELETE query
@@ -472,42 +479,23 @@ check_foreign_key(PG_FUNCTION_ARGS)
472479
char *nv;
473480
int k;
474481

475-
snprintf(sql, sizeof(sql), "update %s set ", relname);
482+
appendStringInfo(&sql, "update %s set ", relname);
476483
for (k = 1; k <= nkeys; k++)
477484
{
478-
int is_char_type = 0;
479-
char *type;
480-
481485
fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
482486
Assert(fn > 0); /* already checked above */
483487
nv = SPI_getvalue(newtuple, tupdesc, fn);
484-
type = SPI_gettype(tupdesc, fn);
485-
486-
if (strcmp(type, "text") == 0 ||
487-
strcmp(type, "varchar") == 0 ||
488-
strcmp(type, "char") == 0 ||
489-
strcmp(type, "bpchar") == 0 ||
490-
strcmp(type, "date") == 0 ||
491-
strcmp(type, "timestamp") == 0)
492-
is_char_type = 1;
493-
#ifdef DEBUG_QUERY
494-
elog(DEBUG4, "check_foreign_key Debug value %s type %s %d",
495-
nv, type, is_char_type);
496-
#endif
497488

498-
/*
499-
* is_char_type =1 i set ' ' for define a new value
500-
*/
501-
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
502-
" %s = %s%s%s %s ",
503-
args2[k], (is_char_type > 0) ? "'" : "",
504-
nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
489+
appendStringInfo(&sql, " %s = %s ",
490+
args2[k], quote_literal_cstr(nv));
491+
if (k < nkeys)
492+
appendStringInfoString(&sql, ", ");
505493
}
506-
strcat(sql, " where ");
494+
appendStringInfoString(&sql, " where ");
507495
}
508496
else
509497
/* DELETE */
510-
snprintf(sql, sizeof(sql), "delete from %s where ", relname);
498+
appendStringInfo(&sql, "delete from %s where ", relname);
511499
}
512500

513501
/*
@@ -518,25 +506,26 @@ check_foreign_key(PG_FUNCTION_ARGS)
518506
*/
519507
else if (action == 's')
520508
{
521-
snprintf(sql, sizeof(sql), "update %s set ", relname);
509+
appendStringInfo(&sql, "update %s set ", relname);
522510
for (i = 1; i <= nkeys; i++)
523511
{
524-
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
525-
"%s = null%s",
526-
args2[i], (i < nkeys) ? ", " : "");
512+
appendStringInfo(&sql, "%s = null", args2[i]);
513+
if (i < nkeys)
514+
appendStringInfoString(&sql, ", ");
527515
}
528-
strcat(sql, " where ");
516+
appendStringInfoString(&sql, " where ");
529517
}
530518

531519
/* Construct WHERE qual */
532520
for (i = 1; i <= nkeys; i++)
533521
{
534-
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
535-
args2[i], i, (i < nkeys) ? "and " : "");
522+
appendStringInfo(&sql, "%s = $%d ", args2[i], i);
523+
if (i < nkeys)
524+
appendStringInfoString(&sql, "and ");
536525
}
537526

538527
/* Prepare plan for query */
539-
pplan = SPI_prepare(sql, nkeys, argtypes);
528+
pplan = SPI_prepare(sql.data, nkeys, argtypes);
540529
if (pplan == NULL)
541530
/* internal error */
542531
elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
@@ -552,11 +541,14 @@ check_foreign_key(PG_FUNCTION_ARGS)
552541
plan->splan[r] = pplan;
553542

554543
args2 += nkeys + 1; /* to the next relation */
544+
545+
#ifdef DEBUG_QUERY
546+
elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql.data);
547+
#endif
548+
549+
pfree(sql.data);
555550
}
556551
plan->nplans = nrefs;
557-
#ifdef DEBUG_QUERY
558-
elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql);
559-
#endif
560552
}
561553

562554
/*

0 commit comments

Comments
 (0)