public inbox for pgsql-bugs@postgresql.org  
help / color / mirror / Atom feed
BUG #19476: Segmentation fault in contrib/spi
8+ messages / 4 participants
[nested] [flat]

* BUG #19476: Segmentation fault in contrib/spi
@ 2026-05-12 14:23 PG Bug reporting form <noreply@postgresql.org>
  2026-05-12 18:04 ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  0 siblings, 1 reply; 8+ messages in thread

From: PG Bug reporting form @ 2026-05-12 14:23 UTC (permalink / raw)
  To: pgsql-bugs@lists.postgresql.org; +Cc: n.kalinin@postgrespro.ru

The following bug has been logged on the website:

Bug reference:      19476
Logged by:          Nikita Kalinin
Email address:      n.kalinin@postgrespro.ru
PostgreSQL version: 18.3
Operating system:   Debian 13
Description:        

If this script is executed on the REL_18_STABLE branch, PostgreSQL crashes
with a segmentation fault:

CREATE EXTENSION refint;
CREATE TABLE c (id int4);
CREATE UNIQUE INDEX ci ON c(id);
CREATE TABLE b (refb int4);
CREATE INDEX bi ON b(refb);

CREATE TRIGGER at AFTER DELETE OR UPDATE ON c FOR EACH ROW
EXECUTE FUNCTION check_foreign_key (1, 'cascade', 'id', 'b', 'refb');

CREATE TRIGGER bt AFTER INSERT OR UPDATE ON b FOR EACH ROW
EXECUTE FUNCTION check_primary_key ('refb', 'c', 'id');

INSERT INTO c VALUES (10);
INSERT INTO b VALUES (10);
UPDATE c SET id = NULL WHERE id = 10;

Backtrace:
#0  __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:76
#1  0x0000563a92ca096e in quote_literal_cstr (rawstr=0x0) at quote.c:109
#2  0x00007fa4a7cce5f8 in check_foreign_key (fcinfo=<optimized out>) at
refint.c:489
#3  0x0000563a929c1d52 in ExecCallTriggerFunc
(trigdata=trigdata@entry=0x7fff8cae1100,
    tgindx=tgindx@entry=0, finfo=finfo@entry=0x563aa5ae4438,
instr=instr@entry=0x0,
    per_tuple_context=per_tuple_context@entry=0x563aa5adb9c0) at
trigger.c:2369
#4  0x0000563a929c4110 in AfterTriggerExecute (estate=<optimized out>,
event=0x563aa5aedbb0,
    relInfo=0x563aa5ae4068, src_relInfo=<optimized out>,
dst_relInfo=<optimized out>,
    trigdesc=0x563aa5ae4278, finfo=0x563aa5ae4438, instr=0x0,
    per_tuple_context=<optimized out>, trig_tuple_slot1=0x0,
trig_tuple_slot2=0x0)
    at trigger.c:4559
#5  afterTriggerInvokeEvents (events=events@entry=0x563aa5a75050,
firing_id=1,
    estate=estate@entry=0x563aa5ae3b20, delete_ok=delete_ok@entry=false) at
trigger.c:4800
#6  0x0000563a929c8e88 in AfterTriggerEndQuery
(estate=estate@entry=0x563aa5ae3b20)
    at trigger.c:5182
#7  0x0000563a929ed504 in standard_ExecutorFinish (queryDesc=0x563aa5996e20)
at execMain.c:443
#8  0x0000563a92bd96f8 in ProcessQuery (plan=0x563aa5af4168,
    sourceText=0x563aa59df7e0 "UPDATE c SET id = NULL WHERE id = 10;",
params=0x0,
    queryEnv=0x0, dest=0x563aa5aeae28, qc=0x7fff8cae1460) at pquery.c:194
#9  0x0000563a92bda41e in PortalRunMulti
(portal=portal@entry=0x563aa5a60510,
    isTopLevel=isTopLevel@entry=true,
setHoldSnapshot=setHoldSnapshot@entry=false,
--Type <RET> for more, q to quit, c to continue without paging--c
    dest=dest@entry=0x563aa5aeae28, altdest=altdest@entry=0x563aa5aeae28,
    qc=qc@entry=0x7fff8cae1460) at pquery.c:1272
#10 0x0000563a92bda7f7 in PortalRun (portal=portal@entry=0x563aa5a60510,
    count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true,
    dest=dest@entry=0x563aa5aeae28, altdest=altdest@entry=0x563aa5aeae28,
    qc=qc@entry=0x7fff8cae1460) at pquery.c:788
#11 0x0000563a92bd63fa in exec_simple_query (
    query_string=0x563aa59df7e0 "UPDATE c SET id = NULL WHERE id = 10;") at
postgres.c:1274
#12 0x0000563a92bd7fc5 in PostgresMain (dbname=<optimized out>,
username=<optimized out>)
    at postgres.c:4770
#13 0x0000563a92bd23bd in BackendMain (startup_data=<optimized out>,
    startup_data_len=<optimized out>) at backend_startup.c:124
#14 0x0000563a92b24932 in postmaster_child_launch (child_type=<optimized
out>, child_slot=1,
    startup_data=startup_data@entry=0x7fff8cae1900,
    startup_data_len=startup_data_len@entry=24,
client_sock=client_sock@entry=0x7fff8cae1920)
    at launch_backend.c:290
#15 0x0000563a92b283d2 in BackendStartup (client_sock=0x7fff8cae1920) at
postmaster.c:3569
#16 ServerLoop () at postmaster.c:1703
#17 0x0000563a92b29ea6 in PostmasterMain (argc=argc@entry=3,
argv=argv@entry=0x563aa5985bf0)
    at postmaster.c:1401
#18 0x0000563a92800a5a in main (argc=3, argv=0x563aa5985bf0) at main.c:227







^ permalink  raw  reply  [nested|flat] 8+ messages in thread

* Re: BUG #19476: Segmentation fault in contrib/spi
  2026-05-12 14:23 BUG #19476: Segmentation fault in contrib/spi PG Bug reporting form <noreply@postgresql.org>
@ 2026-05-12 18:04 ` Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-12 18:52   ` Re: BUG #19476: Segmentation fault in contrib/spi pierre.forstmann@gmail.com
  0 siblings, 1 reply; 8+ messages in thread

From: Ayush Tiwari @ 2026-05-12 18:04 UTC (permalink / raw)
  To: n.kalinin@postgrespro.ru; pgsql-bugs@lists.postgresql.org

Hi,

On Tue, 12 May 2026 at 21:14, PG Bug reporting form <noreply@postgresql.org>
wrote:

>
> CREATE EXTENSION refint;
> CREATE TABLE c (id int4);
> CREATE UNIQUE INDEX ci ON c(id);
> CREATE TABLE b (refb int4);
> CREATE INDEX bi ON b(refb);
>
> CREATE TRIGGER at AFTER DELETE OR UPDATE ON c FOR EACH ROW
> EXECUTE FUNCTION check_foreign_key (1, 'cascade', 'id', 'b', 'refb');
>
> CREATE TRIGGER bt AFTER INSERT OR UPDATE ON b FOR EACH ROW
> EXECUTE FUNCTION check_primary_key ('refb', 'c', 'id');
>
> INSERT INTO c VALUES (10);
> INSERT INTO b VALUES (10);
> UPDATE c SET id = NULL WHERE id = 10;
>
> Backtrace:
> #0  __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:76
> #1  0x0000563a92ca096e in quote_literal_cstr (rawstr=0x0) at quote.c:109
> #2  0x00007fa4a7cce5f8 in check_foreign_key (fcinfo=<optimized out>) at
> refint.c:489
> #3  0x0000563a929c1d52 in ExecCallTriggerFunc
> (trigdata=trigdata@entry=0x7fff8cae1100,
>     tgindx=tgindx@entry=0, finfo=finfo@entry=0x563aa5ae4438,
> instr=instr@entry=0x0,
>     per_tuple_context=per_tuple_context@entry=0x563aa5adb9c0) at
> trigger.c:2369
> #4  0x0000563a929c4110 in AfterTriggerExecute (estate=<optimized out>,
> event=0x563aa5aedbb0,
>     relInfo=0x563aa5ae4068, src_relInfo=<optimized out>,
> dst_relInfo=<optimized out>,
>     trigdesc=0x563aa5ae4278, finfo=0x563aa5ae4438, instr=0x0,
>     per_tuple_context=<optimized out>, trig_tuple_slot1=0x0,
> trig_tuple_slot2=0x0)
>     at trigger.c:4559
> #5  afterTriggerInvokeEvents (events=events@entry=0x563aa5a75050,
> firing_id=1,
>     estate=estate@entry=0x563aa5ae3b20, delete_ok=delete_ok@entry=false)
> at
> trigger.c:4800
> #6  0x0000563a929c8e88 in AfterTriggerEndQuery
> (estate=estate@entry=0x563aa5ae3b20)
>     at trigger.c:5182
> #7  0x0000563a929ed504 in standard_ExecutorFinish
> (queryDesc=0x563aa5996e20)
> at execMain.c:443
> #8  0x0000563a92bd96f8 in ProcessQuery (plan=0x563aa5af4168,
>     sourceText=0x563aa59df7e0 "UPDATE c SET id = NULL WHERE id = 10;",
> params=0x0,
>     queryEnv=0x0, dest=0x563aa5aeae28, qc=0x7fff8cae1460) at pquery.c:194
> #9  0x0000563a92bda41e in PortalRunMulti
> (portal=portal@entry=0x563aa5a60510,
>     isTopLevel=isTopLevel@entry=true,
> setHoldSnapshot=setHoldSnapshot@entry=false,
> --Type <RET> for more, q to quit, c to continue without paging--c
>     dest=dest@entry=0x563aa5aeae28, altdest=altdest@entry=0x563aa5aeae28,
>     qc=qc@entry=0x7fff8cae1460) at pquery.c:1272
> #10 0x0000563a92bda7f7 in PortalRun (portal=portal@entry=0x563aa5a60510,
>     count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry
> =true,
>     dest=dest@entry=0x563aa5aeae28, altdest=altdest@entry=0x563aa5aeae28,
>     qc=qc@entry=0x7fff8cae1460) at pquery.c:788
> #11 0x0000563a92bd63fa in exec_simple_query (
>     query_string=0x563aa59df7e0 "UPDATE c SET id = NULL WHERE id = 10;") at
> postgres.c:1274
> #12 0x0000563a92bd7fc5 in PostgresMain (dbname=<optimized out>,
> username=<optimized out>)
>     at postgres.c:4770
> #13 0x0000563a92bd23bd in BackendMain (startup_data=<optimized out>,
>     startup_data_len=<optimized out>) at backend_startup.c:124
> #14 0x0000563a92b24932 in postmaster_child_launch (child_type=<optimized
> out>, child_slot=1,
>     startup_data=startup_data@entry=0x7fff8cae1900,
>     startup_data_len=startup_data_len@entry=24,
> client_sock=client_sock@entry=0x7fff8cae1920)
>     at launch_backend.c:290
> #15 0x0000563a92b283d2 in BackendStartup (client_sock=0x7fff8cae1920) at
> postmaster.c:3569
> #16 ServerLoop () at postmaster.c:1703
> #17 0x0000563a92b29ea6 in PostmasterMain (argc=argc@entry=3,
> argv=argv@entry=0x563aa5985bf0)
>     at postmaster.c:1401
> #18 0x0000563a92800a5a in main (argc=3, argv=0x563aa5985bf0) at main.c:227
>

Thanks for the report and the clear backtrace.

I had a look at this and I think the immediate crash comes from
check_foreign_key()'s cascade-update branch in contrib/spi/refint.c,
which calls SPI_getvalue() for the new key value and then passes the
result straight into snprintf("%s", ...):

    nv = SPI_getvalue(newtuple, tupdesc, fn);
    ...
    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) ? ", " : "");

For an UPDATE that sets the referenced key to NULL, SPI_getvalue()
returns NULL, so nv is NULL.  On stock glibc/Postgres builds snprintf
substitutes "(null)" and SQL parses that as the NULL keyword, so the
cascade silently does the right thing.  On builds with _FORTIFY_SOURCE
glibc's strlen check dereferences the NULL pointer instead, which
matches the backtrace you posted.

I am attaching a two-patch series that addresses this.

0001 is the minimal fix: emit the SQL NULL keyword when SPI_getvalue()
returns NULL, instead of passing a NULL pointer to snprintf.  This is
small and targeted, and is sufficient on its own to fix the reported
crash.  I did not add a regression test for 0001 because the bug only
manifests on hardened libc builds; on stock builds the bad snprintf
happens to produce a result that the SQL parser accepts.

0002 is a follow-up that I think makes sense but is not strictly
required for this bug.  While looking at the cascade-update path I
noticed two pre-existing issues in the same code:

 - The new key values were embedded into the prepared cascade UPDATE
   query text as literals.  Combined with plan caching (the cache key
   only gained the operation type in 8cfbdf8f4df), the first set of
   new values gets reused for every subsequent cascade UPDATE.

 - Char-like new values were wrapped in single quotes without escaping,
   so a value containing a single quote produced malformed SQL.

0002 switches that branch to use SPI parameters for the new key values
instead of embedding them into the SQL text.  That naturally handles
NULLs via the SPI nulls array, lets a single cached plan handle later
UPDATEs with different keys, and avoids the quoting issue entirely.
It also adds a refint regression test that runs two cascade UPDATEs in
a row (one to NULL, one to a non-NULL value) which catches the cached-
plan reuse.

I'm happy with just 0001 if reviewers prefer to keep the fix minimal,
given that refint is intended mainly as a documentation example
(8cfbdf8f4df explicitly mentioned not back-patching changes here?).

Regards,
Ayush


Attachments:

  [application/octet-stream] v1-0001-Fix-refint-cascade-UPDATE-crash-with-NULL-keys.patch (2.4K, 3-v1-0001-Fix-refint-cascade-UPDATE-crash-with-NULL-keys.patch)
  download | inline diff:
From dd97dd1e196aa8dd0a12afb957b046d984ed491e Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Date: Tue, 12 May 2026 17:43:12 +0000
Subject: [PATCH v1 1/2] Fix refint cascade UPDATE crash with NULL keys

check_foreign_key() builds cascade UPDATE queries using the new key
value retrieved by SPI_getvalue().  When that value is NULL,
SPI_getvalue() returns a NULL pointer, which the code then passed to
snprintf("%s").  That is undefined behavior; on most builds it happens
to print "(null)", which SQL then parses as the NULL keyword, but on
builds with _FORTIFY_SOURCE the snprintf formatter crashes when
computing the string length, matching the backtrace in the report.

Emit the SQL NULL keyword directly when SPI_getvalue() returns NULL.

No regression test is added because triggering the crash requires a
hardened libc; without that, the existing code already happens to
produce the correct result via the "(null)" substitution above.

Reported-by: Nikita Kalinin <n.kalinin@postgrespro.ru>
Discussion: https://postgr.es/m/19476-bd04ea6241345303@postgresql.org
---
 contrib/spi/refint.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
index fbbd558ca1e..0063410f27e 100644
--- a/contrib/spi/refint.c
+++ b/contrib/spi/refint.c
@@ -496,12 +496,22 @@ check_foreign_key(PG_FUNCTION_ARGS)
 #endif
 
 						/*
-						 * is_char_type =1 i set ' ' for define a new value
+						 * is_char_type =1 i set ' ' for define a new value.
+						 *
+						 * SPI_getvalue() returns NULL for SQL NULL values, so
+						 * emit the NULL keyword rather than passing a NULL
+						 * pointer to snprintf("%s"), which is undefined
+						 * behavior.
 						 */
-						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) ? ", " : "");
+						if (nv == NULL)
+							snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
+									 " %s = NULL %s ",
+									 args2[k], (k < nkeys) ? ", " : "");
+						else
+							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) ? ", " : "");
 					}
 					strcat(sql, " where ");
 				}
-- 
2.43.0



  [application/octet-stream] v1-0002-refint-parameterize-cascade-UPDATE-new-key-values.patch (9.3K, 4-v1-0002-refint-parameterize-cascade-UPDATE-new-key-values.patch)
  download | inline diff:
From c4331d930e94803e180f335e0dad5f9b8a0b5717 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Date: Tue, 12 May 2026 17:53:11 +0000
Subject: [PATCH v1 2/2] refint: parameterize cascade UPDATE new key values

Previously, contrib/spi/refint's check_foreign_key() embedded the new
key values into the prepared cascade UPDATE query text as literals.
The preceding patch fixed the immediate NULL crash, but the same code
path has two other latent issues:

* Because the new values were baked into the prepared SQL, the cached
  plan reused the first new key values for every subsequent UPDATE.
  Adding the operation type to the plan cache key (commit 8cfbdf8f4df)
  helped distinguish update vs delete, but the SET RHS was still
  hard-coded into each cached plan.

* Char-like new values were wrapped in single quotes without escaping,
  so a new value containing a single quote produced malformed SQL.

Switch the cascade UPDATE path to use SPI parameters for the new key
values.  This naturally handles NULLs via the SPI nulls array, lets a
single cached plan handle any subsequent UPDATE, and avoids quoting
issues altogether.

Add a refint regression test that runs two cascade UPDATEs in a row to
exercise cached-plan reuse with different key values, including a NULL.

Discussion: https://postgr.es/m/19476-bd04ea6241345303@postgresql.org
---
 contrib/spi/expected/refint.out | 31 +++++++++++++
 contrib/spi/refint.c            | 77 ++++++++++++---------------------
 contrib/spi/sql/refint.sql      | 28 ++++++++++++
 3 files changed, 87 insertions(+), 49 deletions(-)

diff --git a/contrib/spi/expected/refint.out b/contrib/spi/expected/refint.out
index 79633603217..16b8990a80d 100644
--- a/contrib/spi/expected/refint.out
+++ b/contrib/spi/expected/refint.out
@@ -111,3 +111,34 @@ SELECT trigger_name, event_manipulation, event_object_schema, event_object_table
 DROP TABLE pkeys;
 DROP TABLE fkeys;
 DROP TABLE fkeys2;
+-- Cascading updates should use parameters for new key values so that the
+-- cached plan can be reused for subsequent updates with different keys.
+CREATE TABLE c_null (id int4);
+CREATE UNIQUE INDEX c_null_i ON c_null(id);
+CREATE TABLE b_null (refb int4);
+CREATE INDEX b_null_i ON b_null(refb);
+CREATE TRIGGER c_null_fkey_cascade
+	after delete or update on c_null
+	for each row
+	execute function check_foreign_key (1, 'cascade', 'id', 'b_null', 'refb');
+CREATE TRIGGER b_null_pkey_exist
+	after insert or update on b_null
+	for each row
+	execute function check_primary_key ('refb', 'c_null', 'id');
+INSERT INTO c_null VALUES (10);
+INSERT INTO b_null VALUES (10);
+UPDATE c_null SET id = NULL WHERE id = 10;
+NOTICE:  c_null_fkey_cascade: 1 tuple(s) of b_null are updated
+INSERT INTO c_null VALUES (20);
+INSERT INTO b_null VALUES (20);
+UPDATE c_null SET id = 30 WHERE id = 20;
+NOTICE:  c_null_fkey_cascade: 1 tuple(s) of b_null are updated
+SELECT * FROM b_null ORDER BY refb NULLS FIRST;
+ refb 
+------
+     
+   30
+(2 rows)
+
+DROP TABLE c_null;
+DROP TABLE b_null;
diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
index 0063410f27e..bc89015a12c 100644
--- a/contrib/spi/refint.c
+++ b/contrib/spi/refint.c
@@ -248,11 +248,12 @@ check_foreign_key(PG_FUNCTION_ARGS)
 	Trigger    *trigger;		/* to get trigger name */
 	int			nargs;			/* # of args specified in CREATE TRIGGER */
 	char	  **args;			/* arguments: as described above */
-	char	  **args_temp;
 	int			nrefs;			/* number of references (== # of plans) */
 	char		action;			/* 'R'estrict | 'S'etnull | 'C'ascade */
 	int			nkeys;			/* # of key columns */
+	int			nparams;		/* # of query parameters */
 	Datum	   *kvals;			/* key values */
+	char	   *nulls = NULL;	/* null markers for key values */
 	char	   *relname;		/* referencing relation name */
 	Relation	rel;			/* triggered relation */
 	HeapTuple	trigtuple = NULL;	/* tuple to being changed */
@@ -343,7 +344,13 @@ check_foreign_key(PG_FUNCTION_ARGS)
 	 * We use SPI plan preparation feature, so allocate space to place key
 	 * values.
 	 */
-	kvals = (Datum *) palloc(nkeys * sizeof(Datum));
+	nparams = (action == 'c' && is_update) ? 2 * nkeys : nkeys;
+	kvals = (Datum *) palloc(nparams * sizeof(Datum));
+	if (nparams > nkeys)
+	{
+		nulls = (char *) palloc(nparams * sizeof(char));
+		memset(nulls, ' ', nparams * sizeof(char));
+	}
 
 	/*
 	 * Construct ident string as TriggerName $ TriggeredRelationId $
@@ -354,7 +361,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
 
 	/* if there is no plan(s) then allocate argtypes for preparation */
 	if (plan->nplans <= 0)
-		argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
+		argtypes = (Oid *) palloc(nparams * sizeof(Oid));
 
 	/*
 	 * else - check that we have exactly nrefs plan(s) ready
@@ -391,6 +398,9 @@ check_foreign_key(PG_FUNCTION_ARGS)
 			return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
 		}
 
+		if (plan->nplans <= 0)
+			argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
+
 		/*
 		 * If UPDATE then get column value from new tuple being inserted and
 		 * compare is this the same as old one. For the moment we use string
@@ -398,6 +408,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
 		 */
 		if (newtuple != NULL)
 		{
+			bool		newisnull;
 			char	   *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
 			char	   *newval;
 
@@ -408,12 +419,17 @@ check_foreign_key(PG_FUNCTION_ARGS)
 			newval = SPI_getvalue(newtuple, tupdesc, fnumber);
 			if (newval == NULL || strcmp(oldval, newval) != 0)
 				isequal = false;
-		}
 
-		if (plan->nplans <= 0)	/* Get typeId of column */
-			argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
+			if (action == 'c')
+			{
+				kvals[nkeys + i] = SPI_getbinval(newtuple, tupdesc,
+												 fnumber, &newisnull);
+				nulls[nkeys + i] = newisnull ? 'n' : ' ';
+				if (plan->nplans <= 0)
+					argtypes[nkeys + i] = argtypes[i];
+			}
+		}
 	}
-	args_temp = args;
 	nargs -= nkeys;
 	args += nkeys;
 
@@ -468,50 +484,14 @@ check_foreign_key(PG_FUNCTION_ARGS)
 			{
 				if (is_update == 1)
 				{
-					int			fn;
-					char	   *nv;
 					int			k;
 
 					snprintf(sql, sizeof(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.
-						 *
-						 * SPI_getvalue() returns NULL for SQL NULL values, so
-						 * emit the NULL keyword rather than passing a NULL
-						 * pointer to snprintf("%s"), which is undefined
-						 * behavior.
-						 */
-						if (nv == NULL)
-							snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
-									 " %s = NULL %s ",
-									 args2[k], (k < nkeys) ? ", " : "");
-						else
-							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) ? ", " : "");
+						snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
+								 " %s = $%d %s ",
+								 args2[k], nkeys + k, (k < nkeys) ? ", " : "");
 					}
 					strcat(sql, " where ");
 				}
@@ -546,7 +526,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
 			}
 
 			/* Prepare plan for query */
-			pplan = SPI_prepare(sql, nkeys, argtypes);
+			pplan = SPI_prepare(sql, nparams, argtypes);
 			if (pplan == NULL)
 				/* internal error */
 				elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
@@ -591,8 +571,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
 
 		relname = args[0];
 
-		ret = SPI_execp(plan->splan[r], kvals, NULL, tcount);
-		/* we have no NULLs - so we pass   ^^^^  here */
+		ret = SPI_execp(plan->splan[r], kvals, nulls, tcount);
 
 		if (ret < 0)
 			ereport(ERROR,
diff --git a/contrib/spi/sql/refint.sql b/contrib/spi/sql/refint.sql
index 63458127917..d460ad58d53 100644
--- a/contrib/spi/sql/refint.sql
+++ b/contrib/spi/sql/refint.sql
@@ -95,3 +95,31 @@ SELECT trigger_name, event_manipulation, event_object_schema, event_object_table
 DROP TABLE pkeys;
 DROP TABLE fkeys;
 DROP TABLE fkeys2;
+
+-- Cascading updates should use parameters for new key values so that the
+-- cached plan can be reused for subsequent updates with different keys.
+CREATE TABLE c_null (id int4);
+CREATE UNIQUE INDEX c_null_i ON c_null(id);
+CREATE TABLE b_null (refb int4);
+CREATE INDEX b_null_i ON b_null(refb);
+
+CREATE TRIGGER c_null_fkey_cascade
+	after delete or update on c_null
+	for each row
+	execute function check_foreign_key (1, 'cascade', 'id', 'b_null', 'refb');
+
+CREATE TRIGGER b_null_pkey_exist
+	after insert or update on b_null
+	for each row
+	execute function check_primary_key ('refb', 'c_null', 'id');
+
+INSERT INTO c_null VALUES (10);
+INSERT INTO b_null VALUES (10);
+UPDATE c_null SET id = NULL WHERE id = 10;
+INSERT INTO c_null VALUES (20);
+INSERT INTO b_null VALUES (20);
+UPDATE c_null SET id = 30 WHERE id = 20;
+SELECT * FROM b_null ORDER BY refb NULLS FIRST;
+
+DROP TABLE c_null;
+DROP TABLE b_null;
-- 
2.43.0



^ permalink  raw  reply  [nested|flat] 8+ messages in thread

* Re: BUG #19476: Segmentation fault in contrib/spi
  2026-05-12 14:23 BUG #19476: Segmentation fault in contrib/spi PG Bug reporting form <noreply@postgresql.org>
  2026-05-12 18:04 ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
@ 2026-05-12 18:52   ` pierre.forstmann@gmail.com
  2026-05-12 19:27     ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  0 siblings, 1 reply; 8+ messages in thread

From: pierre.forstmann@gmail.com @ 2026-05-12 18:52 UTC (permalink / raw)
  To: pgsql-bugs@lists.postgresql.org

Hello,

You have not used the very last version of refint.c which has been updated just yesterday:

commit 1ebda7da9a43d3ae3564d08612de9cb27fbaf482
Author: Nathan Bossart <nathan@postgresql.org>
Date:   Mon May 11 05:13:48 2026 -0700

    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




I think for patch v1-0001 you need to adapt the code with something like:

                                                 /* SPI_getvalue() returns NULL for SQL NULL values, so
                                                  * emit the NULL keyword rather than passing a NULL
                                                  * pointer to quote_litteral_cstr, which is undefined
                                                  * behavior.
                                                  */
                                                if (nv == NULL)
                                                        appendStringInfo(&sql, " %s = NULL %s",
                                                                                 args2[k], "");
                                                else
                                                        appendStringInfo(&sql, " %s = %s ",
                                                                                 args2[k], quote_literal_cstr(nv));

Regards
Pierre





On 12/05/2026 20:04, Ayush Tiwari <ayushtiwari.slg01@gmail.com> wrote:
> Hi,
> 
> On Tue, 12 May 2026 at 21:14, PG Bug reporting form 
> <noreply@postgresql.org <mailto:noreply@postgresql.org>> wrote:
> 
> 
>     CREATE EXTENSION refint;
>     CREATE TABLE c (id int4);
>     CREATE UNIQUE INDEX ci ON c(id);
>     CREATE TABLE b (refb int4);
>     CREATE INDEX bi ON b(refb);
> 
>     CREATE TRIGGER at AFTER DELETE OR UPDATE ON c FOR EACH ROW
>     EXECUTE FUNCTION check_foreign_key (1, 'cascade', 'id', 'b', 'refb');
> 
>     CREATE TRIGGER bt AFTER INSERT OR UPDATE ON b FOR EACH ROW
>     EXECUTE FUNCTION check_primary_key ('refb', 'c', 'id');
> 
>     INSERT INTO c VALUES (10);
>     INSERT INTO b VALUES (10);
>     UPDATE c SET id = NULL WHERE id = 10;
> 
>     Backtrace:
>     #0  __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:76
>     #1  0x0000563a92ca096e in quote_literal_cstr (rawstr=0x0) at quote.c:109
>     #2  0x00007fa4a7cce5f8 in check_foreign_key (fcinfo=<optimized out>) at
>     refint.c:489
>     #3  0x0000563a929c1d52 in ExecCallTriggerFunc
>     (trigdata=trigdata@entry=0x7fff8cae1100,
>          tgindx=tgindx@entry=0, finfo=finfo@entry=0x563aa5ae4438,
>     instr=instr@entry=0x0,
>          per_tuple_context=per_tuple_context@entry=0x563aa5adb9c0) at
>     trigger.c:2369
>     #4  0x0000563a929c4110 in AfterTriggerExecute (estate=<optimized out>,
>     event=0x563aa5aedbb0,
>          relInfo=0x563aa5ae4068, src_relInfo=<optimized out>,
>     dst_relInfo=<optimized out>,
>          trigdesc=0x563aa5ae4278, finfo=0x563aa5ae4438, instr=0x0,
>          per_tuple_context=<optimized out>, trig_tuple_slot1=0x0,
>     trig_tuple_slot2=0x0)
>          at trigger.c:4559
>     #5  afterTriggerInvokeEvents (events=events@entry=0x563aa5a75050,
>     firing_id=1,
>          estate=estate@entry=0x563aa5ae3b20,
>     delete_ok=delete_ok@entry=false) at
>     trigger.c:4800
>     #6  0x0000563a929c8e88 in AfterTriggerEndQuery
>     (estate=estate@entry=0x563aa5ae3b20)
>          at trigger.c:5182
>     #7  0x0000563a929ed504 in standard_ExecutorFinish
>     (queryDesc=0x563aa5996e20)
>     at execMain.c:443
>     #8  0x0000563a92bd96f8 in ProcessQuery (plan=0x563aa5af4168,
>          sourceText=0x563aa59df7e0 "UPDATE c SET id = NULL WHERE id = 10;",
>     params=0x0,
>          queryEnv=0x0, dest=0x563aa5aeae28, qc=0x7fff8cae1460) at
>     pquery.c:194
>     #9  0x0000563a92bda41e in PortalRunMulti
>     (portal=portal@entry=0x563aa5a60510,
>          isTopLevel=isTopLevel@entry=true,
>     setHoldSnapshot=setHoldSnapshot@entry=false,
>     --Type <RET> for more, q to quit, c to continue without paging--c
>          dest=dest@entry=0x563aa5aeae28,
>     altdest=altdest@entry=0x563aa5aeae28,
>          qc=qc@entry=0x7fff8cae1460) at pquery.c:1272
>     #10 0x0000563a92bda7f7 in PortalRun (portal=portal@entry=0x563aa5a60510,
>          count=count@entry=9223372036854775807,
>     isTopLevel=isTopLevel@entry=true,
>          dest=dest@entry=0x563aa5aeae28,
>     altdest=altdest@entry=0x563aa5aeae28,
>          qc=qc@entry=0x7fff8cae1460) at pquery.c:788
>     #11 0x0000563a92bd63fa in exec_simple_query (
>          query_string=0x563aa59df7e0 "UPDATE c SET id = NULL WHERE id =
>     10;") at
>     postgres.c:1274
>     #12 0x0000563a92bd7fc5 in PostgresMain (dbname=<optimized out>,
>     username=<optimized out>)
>          at postgres.c:4770
>     #13 0x0000563a92bd23bd in BackendMain (startup_data=<optimized out>,
>          startup_data_len=<optimized out>) at backend_startup.c:124
>     #14 0x0000563a92b24932 in postmaster_child_launch (child_type=<optimized
>     out>, child_slot=1,
>          startup_data=startup_data@entry=0x7fff8cae1900,
>          startup_data_len=startup_data_len@entry=24,
>     client_sock=client_sock@entry=0x7fff8cae1920)
>          at launch_backend.c:290
>     #15 0x0000563a92b283d2 in BackendStartup (client_sock=0x7fff8cae1920) at
>     postmaster.c:3569
>     #16 ServerLoop () at postmaster.c:1703
>     #17 0x0000563a92b29ea6 in PostmasterMain (argc=argc@entry=3,
>     argv=argv@entry=0x563aa5985bf0)
>          at postmaster.c:1401
>     #18 0x0000563a92800a5a in main (argc=3, argv=0x563aa5985bf0) at
>     main.c:227
> 
> 
> Thanks for the report and the clear backtrace.
> 
> I had a look at this and I think the immediate crash comes from
> check_foreign_key()'s cascade-update branch in contrib/spi/refint.c,
> which calls SPI_getvalue() for the new key value and then passes the
> result straight into snprintf("%s", ...):
> 
>      nv = SPI_getvalue(newtuple, tupdesc, fn);
>      ...
>      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) ? ", " : "");
> 
> For an UPDATE that sets the referenced key to NULL, SPI_getvalue()
> returns NULL, so nv is NULL.  On stock glibc/Postgres builds snprintf
> substitutes "(null)" and SQL parses that as the NULL keyword, so the
> cascade silently does the right thing.  On builds with _FORTIFY_SOURCE
> glibc's strlen check dereferences the NULL pointer instead, which
> matches the backtrace you posted.
> 
> I am attaching a two-patch series that addresses this.
> 
> 0001 is the minimal fix: emit the SQL NULL keyword when SPI_getvalue()
> returns NULL, instead of passing a NULL pointer to snprintf.  This is
> small and targeted, and is sufficient on its own to fix the reported
> crash.  I did not add a regression test for 0001 because the bug only
> manifests on hardened libc builds; on stock builds the bad snprintf
> happens to produce a result that the SQL parser accepts.
> 
> 0002 is a follow-up that I think makes sense but is not strictly
> required for this bug.  While looking at the cascade-update path I
> noticed two pre-existing issues in the same code:
> 
>   - The new key values were embedded into the prepared cascade UPDATE
>     query text as literals.  Combined with plan caching (the cache key
>     only gained the operation type in 8cfbdf8f4df), the first set of
>     new values gets reused for every subsequent cascade UPDATE.
> 
>   - Char-like new values were wrapped in single quotes without escaping,
>     so a value containing a single quote produced malformed SQL.
> 
> 0002 switches that branch to use SPI parameters for the new key values
> instead of embedding them into the SQL text.  That naturally handles
> NULLs via the SPI nulls array, lets a single cached plan handle later
> UPDATEs with different keys, and avoids the quoting issue entirely.
> It also adds a refint regression test that runs two cascade UPDATEs in
> a row (one to NULL, one to a non-NULL value) which catches the cached-
> plan reuse.
> 
> I'm happy with just 0001 if reviewers prefer to keep the fix minimal,
> given that refint is intended mainly as a documentation example
> (8cfbdf8f4df explicitly mentioned not back-patching changes here?).
> 
> Regards,
> Ayush
> 





^ permalink  raw  reply  [nested|flat] 8+ messages in thread

* Re: BUG #19476: Segmentation fault in contrib/spi
  2026-05-12 14:23 BUG #19476: Segmentation fault in contrib/spi PG Bug reporting form <noreply@postgresql.org>
  2026-05-12 18:04 ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-12 18:52   ` Re: BUG #19476: Segmentation fault in contrib/spi pierre.forstmann@gmail.com
@ 2026-05-12 19:27     ` Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-14 16:01       ` Re: BUG #19476: Segmentation fault in contrib/spi Nathan Bossart <nathandbossart@gmail.com>
  0 siblings, 1 reply; 8+ messages in thread

From: Ayush Tiwari @ 2026-05-12 19:27 UTC (permalink / raw)
  To: pierre.forstmann@gmail.com; +Cc: pgsql-bugs@lists.postgresql.org

Hi,


On Wed, 13 May 2026 at 00:22, <pierre.forstmann@gmail.com> wrote:

> Hello,
>
> You have not used the very last version of refint.c which has been updated
> just yesterday:
>
> commit 1ebda7da9a43d3ae3564d08612de9cb27fbaf482
> Author: Nathan Bossart <nathan@postgresql.org>
> Date:   Mon May 11 05:13:48 2026 -0700
>
>     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
>
>
You're right, thanks for catching this.  I sent the v1 patches against
master from the day before; commit 260e97733bf (CVE-2026-6637) landed
in between and I had not noticed it.  That commit rewrites the same
cascade-update path to use StringInfo and quote_literal_cstr(), so the
v1 patches do not apply on current master at all.

Importantly, after 260e97733bf the bug is also no longer dependent on
_FORTIFY_SOURCE: the new code calls quote_literal_cstr(nv) directly,
which dereferences nv via strlen() and segfaults on stock builds too.
I reproduced this on plain master built with --enable-cassert.

I have rebased the minimal fix on current master.  It is essentially
the same shape as the snippet you suggested -- emit the NULL keyword
directly when SPI_getvalue() returns NULL, otherwise pass through
quote_literal_cstr() as today.  Attached as v2-0001.

I dropped my earlier 0002 patch.  The CVE fix already addressed the
quoting/escaping concerns that motivated half of it.

Regards,
Ayush


Attachments:

  [application/octet-stream] v2-0001-Fix-refint-cascade-UPDATE-crash-with-NULL-keys.patch (1.8K, 3-v2-0001-Fix-refint-cascade-UPDATE-crash-with-NULL-keys.patch)
  download | inline diff:
From 1f2b319204ba0fae0fbf105e1eb531879c67294b Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Date: Tue, 12 May 2026 19:20:08 +0000
Subject: [PATCH v2] Fix refint cascade UPDATE crash with NULL keys

check_foreign_key() builds cascade UPDATE queries using the new key
value retrieved by SPI_getvalue().  After commit 260e97733bf
(CVE-2026-6637) it passes that value through quote_literal_cstr() to
properly escape literals in the generated SQL.

When the new key value is NULL, however, SPI_getvalue() returns a NULL
pointer, which quote_literal_cstr() then dereferences in its strlen()
call, crashing the backend.

Emit the SQL NULL keyword directly when SPI_getvalue() returns NULL.

Reported-by: Nikita Kalinin <n.kalinin@postgrespro.ru>
Discussion: https://postgr.es/m/19476-bd04ea6241345303@postgresql.org
---
 contrib/spi/refint.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
index c44c87bcd96..5428b511c16 100644
--- a/contrib/spi/refint.c
+++ b/contrib/spi/refint.c
@@ -486,8 +486,17 @@ check_foreign_key(PG_FUNCTION_ARGS)
 						Assert(fn > 0); /* already checked above */
 						nv = SPI_getvalue(newtuple, tupdesc, fn);
 
-						appendStringInfo(&sql, " %s = %s ",
-										 args2[k], quote_literal_cstr(nv));
+						/*
+						 * SPI_getvalue() returns NULL for SQL NULL values.
+						 * Emit the NULL keyword directly rather than passing
+						 * a NULL pointer to quote_literal_cstr(), which would
+						 * dereference it.
+						 */
+						if (nv == NULL)
+							appendStringInfo(&sql, " %s = NULL ", args2[k]);
+						else
+							appendStringInfo(&sql, " %s = %s ",
+											 args2[k], quote_literal_cstr(nv));
 						if (k < nkeys)
 							appendStringInfoString(&sql, ", ");
 					}
-- 
2.43.0



^ permalink  raw  reply  [nested|flat] 8+ messages in thread

* Re: BUG #19476: Segmentation fault in contrib/spi
  2026-05-12 14:23 BUG #19476: Segmentation fault in contrib/spi PG Bug reporting form <noreply@postgresql.org>
  2026-05-12 18:04 ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-12 18:52   ` Re: BUG #19476: Segmentation fault in contrib/spi pierre.forstmann@gmail.com
  2026-05-12 19:27     ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
@ 2026-05-14 16:01       ` Nathan Bossart <nathandbossart@gmail.com>
  2026-05-14 16:45         ` Re: BUG #19476: Segmentation fault in contrib/spi Nathan Bossart <nathandbossart@gmail.com>
  0 siblings, 1 reply; 8+ messages in thread

From: Nathan Bossart @ 2026-05-14 16:01 UTC (permalink / raw)
  To: Ayush Tiwari <ayushtiwari.slg01@gmail.com>; +Cc: pierre.forstmann@gmail.com; pgsql-bugs@lists.postgresql.org

On Wed, May 13, 2026 at 12:57:47AM +0530, Ayush Tiwari wrote:
> I have rebased the minimal fix on current master.  It is essentially
> the same shape as the snippet you suggested -- emit the NULL keyword
> directly when SPI_getvalue() returns NULL, otherwise pass through
> quote_literal_cstr() as today.  Attached as v2-0001.
> 
> I dropped my earlier 0002 patch.  The CVE fix already addressed the
> quoting/escaping concerns that motivated half of it.

I'm confused why you dropped 0002.  Reusing the new key values for
subsequent updates seems like a bug worth fixing.  However, note that the
parameter symbol approach doesn't work well for cases like this:

    CREATE EXTENSION refint;

    CREATE TABLE p (a int);
    CREATE TABLE f (a xid);
    CREATE TRIGGER t
        AFTER DELETE OR UPDATE ON p
        FOR EACH ROW EXECUTE PROCEDURE
        check_foreign_key(1, 'c', 'a', 'f', 'a');

    INSERT INTO p VALUES (1);
    UPDATE p SET a = 2;

With a rebased version of 0002 applied, the UPDATE statement fails like
this:

    ERROR:  column "a" is of type xid but expression is of type integer
    LINE 1: update f set  a = $2  where a = $1
                              ^
    HINT:  You will need to rewrite or cast the expression.
    QUERY:  update f set  a = $2  where a = $1

Presumably the problem is that we're using the wrong argument type for the
foreign key.  I'm not sure this is trivial to fix; it seems like we'd need
to provide that information in CREATE TRIGGER or look up the foreign key
type within the trigger function itself.  Perhaps the best we can do is to
avoid caching a plan in this case.

Regarding 0001, note that the refint docs state the following:

    Note that the primary/unique key columns should be marked NOT NULL and
    should have a unique index.

So maybe we could alternatively teach check_foreign_key() to either ERROR
or do nothing instead.  On the other hand, given this case seemed to
accidentally work before the CVE fix, it's arguably worth fixing.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 8+ messages in thread

* Re: BUG #19476: Segmentation fault in contrib/spi
  2026-05-12 14:23 BUG #19476: Segmentation fault in contrib/spi PG Bug reporting form <noreply@postgresql.org>
  2026-05-12 18:04 ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-12 18:52   ` Re: BUG #19476: Segmentation fault in contrib/spi pierre.forstmann@gmail.com
  2026-05-12 19:27     ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-14 16:01       ` Re: BUG #19476: Segmentation fault in contrib/spi Nathan Bossart <nathandbossart@gmail.com>
@ 2026-05-14 16:45         ` Nathan Bossart <nathandbossart@gmail.com>
  2026-05-14 17:12           ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  0 siblings, 1 reply; 8+ messages in thread

From: Nathan Bossart @ 2026-05-14 16:45 UTC (permalink / raw)
  To: Ayush Tiwari <ayushtiwari.slg01@gmail.com>; +Cc: pierre.forstmann@gmail.com; pgsql-bugs@lists.postgresql.org

On Thu, May 14, 2026 at 11:01:55AM -0500, Nathan Bossart wrote:
> Regarding 0001, note that the refint docs state the following:
> 
>     Note that the primary/unique key columns should be marked NOT NULL and
>     should have a unique index.
> 
> So maybe we could alternatively teach check_foreign_key() to either ERROR
> or do nothing instead.  On the other hand, given this case seemed to
> accidentally work before the CVE fix, it's arguably worth fixing.

Here is what I have staged for commit, which I intend to do shortly.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 8+ messages in thread

* Re: BUG #19476: Segmentation fault in contrib/spi
  2026-05-12 14:23 BUG #19476: Segmentation fault in contrib/spi PG Bug reporting form <noreply@postgresql.org>
  2026-05-12 18:04 ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-12 18:52   ` Re: BUG #19476: Segmentation fault in contrib/spi pierre.forstmann@gmail.com
  2026-05-12 19:27     ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-14 16:01       ` Re: BUG #19476: Segmentation fault in contrib/spi Nathan Bossart <nathandbossart@gmail.com>
  2026-05-14 16:45         ` Re: BUG #19476: Segmentation fault in contrib/spi Nathan Bossart <nathandbossart@gmail.com>
@ 2026-05-14 17:12           ` Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-14 18:16             ` Re: BUG #19476: Segmentation fault in contrib/spi Nathan Bossart <nathandbossart@gmail.com>
  0 siblings, 1 reply; 8+ messages in thread

From: Ayush Tiwari @ 2026-05-14 17:12 UTC (permalink / raw)
  To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: pierre.forstmann@gmail.com; pgsql-bugs@lists.postgresql.org

Hi,

On Thu, 14 May 2026 at 22:15, Nathan Bossart <nathandbossart@gmail.com>
wrote:

> On Thu, May 14, 2026 at 11:01:55AM -0500, Nathan Bossart wrote:
> > Regarding 0001, note that the refint docs state the following:
> >
> >     Note that the primary/unique key columns should be marked NOT NULL
> and
> >     should have a unique index.
> >
> > So maybe we could alternatively teach check_foreign_key() to either ERROR
> > or do nothing instead.  On the other hand, given this case seemed to
> > accidentally work before the CVE fix, it's arguably worth fixing.
>
> Here is what I have staged for commit, which I intend to do shortly.
>
>
Thanks, this version looks good to me.  The compact form is fine, and I
agree with preserving the pre-CVE behavior for this misconfigured case
rather than turning it into an ERROR or no-op.

I also agree with your earlier point about the parameterized 0002: using
the triggered relation's key types for parameters is not generally right
for the referencing relation's SET targets.

I think the cached cascade UPDATE plan issue is worth pursuing,
I'll start a different hackers thread on that
probably using the "don't cache cascade UPDATE plans" approach you
suggested.

Regards,
Ayush


^ permalink  raw  reply  [nested|flat] 8+ messages in thread

* Re: BUG #19476: Segmentation fault in contrib/spi
  2026-05-12 14:23 BUG #19476: Segmentation fault in contrib/spi PG Bug reporting form <noreply@postgresql.org>
  2026-05-12 18:04 ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-12 18:52   ` Re: BUG #19476: Segmentation fault in contrib/spi pierre.forstmann@gmail.com
  2026-05-12 19:27     ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
  2026-05-14 16:01       ` Re: BUG #19476: Segmentation fault in contrib/spi Nathan Bossart <nathandbossart@gmail.com>
  2026-05-14 16:45         ` Re: BUG #19476: Segmentation fault in contrib/spi Nathan Bossart <nathandbossart@gmail.com>
  2026-05-14 17:12           ` Re: BUG #19476: Segmentation fault in contrib/spi Ayush Tiwari <ayushtiwari.slg01@gmail.com>
@ 2026-05-14 18:16             ` Nathan Bossart <nathandbossart@gmail.com>
  0 siblings, 0 replies; 8+ messages in thread

From: Nathan Bossart @ 2026-05-14 18:16 UTC (permalink / raw)
  To: Ayush Tiwari <ayushtiwari.slg01@gmail.com>; +Cc: pierre.forstmann@gmail.com; pgsql-bugs@lists.postgresql.org

On Thu, May 14, 2026 at 10:42:07PM +0530, Ayush Tiwari wrote:
> Thanks, this version looks good to me.  The compact form is fine, and I
> agree with preserving the pre-CVE behavior for this misconfigured case
> rather than turning it into an ERROR or no-op.

Committed.

> I think the cached cascade UPDATE plan issue is worth pursuing,
> I'll start a different hackers thread on that
> probably using the "don't cache cascade UPDATE plans" approach you
> suggested.

Great, thanks.

-- 
nathan






^ permalink  raw  reply  [nested|flat] 8+ messages in thread


end of thread, other threads:[~2026-05-14 18:16 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-05-12 14:23 BUG #19476: Segmentation fault in contrib/spi PG Bug reporting form <noreply@postgresql.org>
2026-05-12 18:04 ` Ayush Tiwari <ayushtiwari.slg01@gmail.com>
2026-05-12 18:52   ` pierre.forstmann@gmail.com
2026-05-12 19:27     ` Ayush Tiwari <ayushtiwari.slg01@gmail.com>
2026-05-14 16:01       ` Nathan Bossart <nathandbossart@gmail.com>
2026-05-14 16:45         ` Nathan Bossart <nathandbossart@gmail.com>
2026-05-14 17:12           ` Ayush Tiwari <ayushtiwari.slg01@gmail.com>
2026-05-14 18:16             ` Nathan Bossart <nathandbossart@gmail.com>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox