public inbox for pgsql-bugs@postgresql.org
help / color / mirror / Atom feedBUG #19484: Segmentation fault triggered by FDW
4+ messages / 4 participants
[nested] [flat]
* BUG #19484: Segmentation fault triggered by FDW
@ 2026-05-18 06:38 PG Bug reporting form <noreply@postgresql.org>
0 siblings, 1 reply; 4+ messages in thread
From: PG Bug reporting form @ 2026-05-18 06:38 UTC (permalink / raw)
To: pgsql-bugs@lists.postgresql.org; +Cc: 798604270@qq.com
The following bug has been logged on the website:
Bug reference: 19484
Logged by: Chi Zhang
Email address: 798604270@qq.com
PostgreSQL version: 18.4
Operating system: Ubuntu 24.04
Description:
Hi,
I found the following test case triggers a segmentation fault:
```
\set ON_ERROR_STOP on
CREATE EXTENSION postgres_fdw;
CREATE SERVER loopback FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (
host '/path/to/pg_socket',
port '5432',
dbname :'dbname'
);
CREATE USER MAPPING FOR postgres SERVER loopback
OPTIONS (user 'postgres');
CREATE SCHEMA r;
CREATE TABLE r.remote_p2 (a int NOT NULL, b int);
CREATE TABLE pt (a int NOT NULL, b int) PARTITION BY LIST (a);
CREATE TABLE pt_p1 PARTITION OF pt FOR VALUES IN (1);
CREATE FOREIGN TABLE pt_p2 PARTITION OF pt FOR VALUES IN (2)
SERVER loopback
OPTIONS (schema_name 'r', table_name 'remote_p2');
INSERT INTO pt_p1 VALUES (1, 10);
INSERT INTO r.remote_p2 VALUES (2, 20);
SET plan_cache_mode = force_generic_plan;
PREPARE upd(int) AS
UPDATE pt
SET b = b + 1
WHERE a = $1
RETURNING tableoid::regclass, a, b;
EXPLAIN (costs off) EXECUTE upd(2);
EXECUTE upd(2);
SELECT * FROM r.remote_p2 ORDER BY a;
```
This is the log:
```
2026-05-18 13:40:41.888 CST [21729] LOG: database system is ready to accept
connections
2026-05-18 13:41:03.317 CST [21932] LOG: unexpected EOF on client
connection with an open transaction
2026-05-18 13:41:03.317 CST [21729] LOG: client backend (PID 21931) was
terminated by signal 11: Segmentation fault
2026-05-18 13:41:03.317 CST [21729] DETAIL: Failed process was running:
EXECUTE upd(2);
2026-05-18 13:41:03.317 CST [21729] LOG: terminating any other active
server processes
2026-05-18 13:41:03.319 CST [21729] LOG: all server processes terminated;
reinitializing
2026-05-18 13:41:03.345 CST [21936] LOG: database system was interrupted;
last known up at 2026-05-18 13:40:41 CST
2026-05-18 13:41:03.509 CST [21936] LOG: database system was not properly
shut down; automatic recovery in progress
2026-05-18 13:41:03.513 CST [21936] LOG: redo starts at 0/98371040
2026-05-18 13:41:03.531 CST [21936] LOG: invalid record length at
0/987B6E68: expected at least 24, got 0
2026-05-18 13:41:03.531 CST [21936] LOG: redo done at 0/987B6E40 system
usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.01 s
2026-05-18 13:41:03.537 CST [21937] LOG: checkpoint starting:
end-of-recovery fast wait
2026-05-18 13:41:03.654 CST [21937] LOG: checkpoint complete:
end-of-recovery fast wait: wrote 975 buffers (6.0%), wrote 3 SLRU buffers; 0
WAL file(s) added, 0 removed, 0
recycled; write=0.081 s, sync=0.030 s, total=0.121 s; sync files=325,
longest=0.005 s, average=0.001 s; distance=4375 kB, estimate=4375 kB;
lsn=0/987B6E68, redo lsn=0/987B6E68
2026-05-18 13:41:03.660 CST [21729] LOG: database system is ready to
accept connections
```
I built the Postgres from source code
901ed9b352b41f034e17bc540725082a488fce31 of github commit.
^ permalink raw reply [nested|flat] 4+ messages in thread
* Re: BUG #19484: Segmentation fault triggered by FDW
@ 2026-05-20 12:37 Ayush Tiwari <ayushtiwari.slg01@gmail.com>
parent: PG Bug reporting form <noreply@postgresql.org>
0 siblings, 2 replies; 4+ messages in thread
From: Ayush Tiwari @ 2026-05-20 12:37 UTC (permalink / raw)
To: 798604270@qq.com; pgsql-bugs@lists.postgresql.org; Etsuro Fujita <etsuro.fujita@gmail.com>
Hi,
On Wed, 20 May 2026 at 03:59, PG Bug reporting form <noreply@postgresql.org>
wrote:
> The following bug has been logged on the website:
>
> Bug reference: 19484
> Logged by: Chi Zhang
> Email address: 798604270@qq.com
> PostgreSQL version: 18.4
> Operating system: Ubuntu 24.04
> Description:
>
> Hi,
>
> I found the following test case triggers a segmentation fault:
>
> ```
> \set ON_ERROR_STOP on
>
> CREATE EXTENSION postgres_fdw;
>
> CREATE SERVER loopback FOREIGN DATA WRAPPER postgres_fdw
> OPTIONS (
> host '/path/to/pg_socket',
> port '5432',
> dbname :'dbname'
> );
>
> CREATE USER MAPPING FOR postgres SERVER loopback
> OPTIONS (user 'postgres');
>
> CREATE SCHEMA r;
> CREATE TABLE r.remote_p2 (a int NOT NULL, b int);
>
> CREATE TABLE pt (a int NOT NULL, b int) PARTITION BY LIST (a);
> CREATE TABLE pt_p1 PARTITION OF pt FOR VALUES IN (1);
> CREATE FOREIGN TABLE pt_p2 PARTITION OF pt FOR VALUES IN (2)
> SERVER loopback
> OPTIONS (schema_name 'r', table_name 'remote_p2');
>
> INSERT INTO pt_p1 VALUES (1, 10);
> INSERT INTO r.remote_p2 VALUES (2, 20);
>
> SET plan_cache_mode = force_generic_plan;
>
> PREPARE upd(int) AS
> UPDATE pt
> SET b = b + 1
> WHERE a = $1
> RETURNING tableoid::regclass, a, b;
>
> EXPLAIN (costs off) EXECUTE upd(2);
> EXECUTE upd(2);
> SELECT * FROM r.remote_p2 ORDER BY a;
>
Thanks for the very precise repro, that made this easy to track down.
I reproduced the crash on master. The plan EXPLAIN under
force_generic_plan shows runtime pruning is in effect:
Update on pt
Foreign Update on pt_p2 pt_2
-> Append
Subplans Removed: 1
-> Foreign Update on pt_p2 pt_2
The SEGV happens inside postgresBeginForeignModify() because
ExecInitModifyTable() builds re-indexed "kept" copies of several
parallel per-result-relation lists after dropping pruned relations -
withCheckOptionLists, returningLists, updateColnosLists,
mergeActionLists and mergeJoinConditions, however two members were
missed:
- node->fdwPrivLists, read with list_nth(node->fdwPrivLists, i) when
BeginForeignModify() is called, and
- node->fdwDirectModifyPlans, checked with bms_is_member(i, ...) when
setting ri_usesFdwDirectModify.
Both were still indexed against the original (pre-pruning) positions
while the surrounding loop's "i" is now the kept position. When the
foreign partition's kept-index no longer matched its original index,
BeginForeignModify() got the wrong fdw_private and crashed.
Attached patch builds re-indexed kept copies for these two arrays in
the same loop as the other parallel lists, and uses them at the two
call sites.
Regards,
Ayush
Attachments:
[application/octet-stream] v1-0001-Re-index-ModifyTable-FDW-arrays-when-pruning-resu.patch (7.3K, 3-v1-0001-Re-index-ModifyTable-FDW-arrays-when-pruning-resu.patch)
download | inline diff:
From 1bcf981c29f54b77a07c25a7b3eb06d90164bd8a Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Date: Wed, 20 May 2026 05:06:57 +0000
Subject: [PATCH v1] Re-index ModifyTable FDW arrays when pruning result
relations
ExecInitModifyTable() copies parallel per-result-relation lists from
the plan node into a new "kept" set after dropping pruned result
relations. That re-indexing was already done for withCheckOptionLists,
returningLists, updateColnosLists, mergeActionLists and
mergeJoinConditions, but two members were missed:
* node->fdwPrivLists, indexed by list_nth() when calling
BeginForeignModify(), and
* node->fdwDirectModifyPlans, indexed by bms_is_member() when setting
ri_usesFdwDirectModify.
Both were still read using the *kept* loop variable i against the
*original* (pre-pruning) indexing, so on a partitioned UPDATE/DELETE
that uses a generic plan (PREPARE/EXECUTE under plan_cache_mode =
force_generic_plan) and runtime partition pruning, a foreign partition
whose original index no longer matched its kept position caused
BeginForeignModify() to receive the wrong fdw_private and segfault
inside the FDW.
Build re-indexed kept copies for these two arrays in the same loop as
the other parallel lists and use them at the call sites. Add a
postgres_fdw regression case using PREPARE/EXECUTE under
force_generic_plan that exercises the failing path.
Reported-by: Chi Zhang <798604270@qq.com>
Discussion: https://postgr.es/m/19484-...@postgresql.org
---
.../postgres_fdw/expected/postgres_fdw.out | 26 +++++++++++++++++++
contrib/postgres_fdw/sql/postgres_fdw.sql | 22 ++++++++++++++++
src/backend/executor/nodeModifyTable.c | 18 +++++++++++--
3 files changed, 64 insertions(+), 2 deletions(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index e90289e4ab1..872d871a675 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9337,6 +9337,32 @@ select tableoid::regclass, * FROM locp;
-- The executor should not let unexercised FDWs shut down
update utrtest set a = 1 where b = 'foo';
+-- Runtime pruning of result relations must keep ModifyTable's per-relation
+-- FDW arrays (fdwPrivLists, fdwDirectModifyPlans) aligned with the kept
+-- resultRelations. Otherwise BeginForeignModify() reads the wrong
+-- fdw_private and segfaults.
+create table fdw_part_update (a int not null, b int) partition by list (a);
+create table fdw_part_update_p1 partition of fdw_part_update for values in (1);
+create table fdw_part_update_remote (a int not null, b int);
+create foreign table fdw_part_update_p2 partition of fdw_part_update
+ for values in (2)
+ server loopback options (table_name 'fdw_part_update_remote');
+insert into fdw_part_update_p1 values (1, 10);
+insert into fdw_part_update_remote values (2, 20);
+set plan_cache_mode = force_generic_plan;
+prepare fdw_part_upd(int) as
+ update fdw_part_update set b = b + 1 where a = $1
+ returning tableoid::regclass, a, b;
+execute fdw_part_upd(2);
+ tableoid | a | b
+--------------------+---+----
+ fdw_part_update_p2 | 2 | 21
+(1 row)
+
+deallocate fdw_part_upd;
+reset plan_cache_mode;
+drop table fdw_part_update;
+drop table fdw_part_update_remote;
-- Test that remote triggers work with update tuple routing
create trigger loct_br_insert_trigger before insert on loct
for each row execute procedure br_insert_trigfunc();
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index dfc58beb0d2..c80aaf1c1b4 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2723,6 +2723,28 @@ select tableoid::regclass, * FROM locp;
-- The executor should not let unexercised FDWs shut down
update utrtest set a = 1 where b = 'foo';
+-- Runtime pruning of result relations must keep ModifyTable's per-relation
+-- FDW arrays (fdwPrivLists, fdwDirectModifyPlans) aligned with the kept
+-- resultRelations. Otherwise BeginForeignModify() reads the wrong
+-- fdw_private and segfaults.
+create table fdw_part_update (a int not null, b int) partition by list (a);
+create table fdw_part_update_p1 partition of fdw_part_update for values in (1);
+create table fdw_part_update_remote (a int not null, b int);
+create foreign table fdw_part_update_p2 partition of fdw_part_update
+ for values in (2)
+ server loopback options (table_name 'fdw_part_update_remote');
+insert into fdw_part_update_p1 values (1, 10);
+insert into fdw_part_update_remote values (2, 20);
+set plan_cache_mode = force_generic_plan;
+prepare fdw_part_upd(int) as
+ update fdw_part_update set b = b + 1 where a = $1
+ returning tableoid::regclass, a, b;
+execute fdw_part_upd(2);
+deallocate fdw_part_upd;
+reset plan_cache_mode;
+drop table fdw_part_update;
+drop table fdw_part_update_remote;
+
-- Test that remote triggers work with update tuple routing
create trigger loct_br_insert_trigger before insert on loct
for each row execute procedure br_insert_trigfunc();
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 478cb01783c..f69060cb5ab 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -5108,6 +5108,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
List *resultRelations = NIL;
List *withCheckOptionLists = NIL;
List *returningLists = NIL;
+ /* fdwPrivLists/fdwDirectModifyPlans are re-indexed to match resultRelations */
+ List *fdwPrivLists = NIL;
+ Bitmapset *fdwDirectModifyPlans = NULL;
List *updateColnosLists = NIL;
List *mergeActionLists = NIL;
List *mergeJoinConditions = NIL;
@@ -5153,6 +5156,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (keep_rel)
{
+ int new_index = list_length(resultRelations);
+
resultRelations = lappend_int(resultRelations, rti);
if (node->withCheckOptionLists)
{
@@ -5170,6 +5175,15 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
returningLists = lappend(returningLists, returningList);
}
+ if (node->fdwPrivLists)
+ {
+ List *fdwPrivList = (List *) list_nth(node->fdwPrivLists, i);
+
+ fdwPrivLists = lappend(fdwPrivLists, fdwPrivList);
+ }
+ if (bms_is_member(i, node->fdwDirectModifyPlans))
+ fdwDirectModifyPlans = bms_add_member(fdwDirectModifyPlans,
+ new_index);
if (node->updateColnosLists)
{
List *updateColnosList = list_nth(node->updateColnosLists, i);
@@ -5291,7 +5305,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/* Initialize the usesFdwDirectModify flag */
resultRelInfo->ri_usesFdwDirectModify =
- bms_is_member(i, node->fdwDirectModifyPlans);
+ bms_is_member(i, fdwDirectModifyPlans);
/*
* Verify result relation is a valid target for the current operation
@@ -5320,7 +5334,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
resultRelInfo->ri_FdwRoutine != NULL &&
resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
{
- List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
+ List *fdw_private = (List *) list_nth(fdwPrivLists, i);
resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
resultRelInfo,
--
2.43.0
^ permalink raw reply [nested|flat] 4+ messages in thread
* Re: BUG #19484: Segmentation fault triggered by FDW
@ 2026-05-20 17:46 Etsuro Fujita <etsuro.fujita@gmail.com>
parent: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
1 sibling, 0 replies; 4+ messages in thread
From: Etsuro Fujita @ 2026-05-20 17:46 UTC (permalink / raw)
To: Ayush Tiwari <ayushtiwari.slg01@gmail.com>; +Cc: 798604270@qq.com; pgsql-bugs@lists.postgresql.org
Hi,
On Wed, May 20, 2026 at 5:37 AM Ayush Tiwari
<ayushtiwari.slg01@gmail.com> wrote:
> On Wed, 20 May 2026 at 03:59, PG Bug reporting form <noreply@postgresql.org> wrote:
>> I found the following test case triggers a segmentation fault:
[snip]
> Thanks for the very precise repro, that made this easy to track down.
>
> I reproduced the crash on master. The plan EXPLAIN under
> force_generic_plan shows runtime pruning is in effect:
>
> Update on pt
> Foreign Update on pt_p2 pt_2
> -> Append
> Subplans Removed: 1
> -> Foreign Update on pt_p2 pt_2
>
> The SEGV happens inside postgresBeginForeignModify() because
> ExecInitModifyTable() builds re-indexed "kept" copies of several
> parallel per-result-relation lists after dropping pruned relations -
> withCheckOptionLists, returningLists, updateColnosLists,
> mergeActionLists and mergeJoinConditions, however two members were
> missed:
>
> - node->fdwPrivLists, read with list_nth(node->fdwPrivLists, i) when
> BeginForeignModify() is called, and
> - node->fdwDirectModifyPlans, checked with bms_is_member(i, ...) when
> setting ri_usesFdwDirectModify.
>
> Both were still indexed against the original (pre-pruning) positions
> while the surrounding loop's "i" is now the kept position. When the
> foreign partition's kept-index no longer matched its original index,
> BeginForeignModify() got the wrong fdw_private and crashed.
>
> Attached patch builds re-indexed kept copies for these two arrays in
> the same loop as the other parallel lists, and uses them at the two
> call sites.
Thanks Chi for the report, and Ayush for the analysis and patch! Will review.
Best regards,
Etsuro Fujita
^ permalink raw reply [nested|flat] 4+ messages in thread
* Re: BUG #19484: Segmentation fault triggered by FDW
@ 2026-05-22 20:56 Matheus Alcantara <matheusssilv97@gmail.com>
parent: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
1 sibling, 0 replies; 4+ messages in thread
From: Matheus Alcantara @ 2026-05-22 20:56 UTC (permalink / raw)
To: Ayush Tiwari <ayushtiwari.slg01@gmail.com>; 798604270@qq.com; pgsql-bugs@lists.postgresql.org; Etsuro Fujita <etsuro.fujita@gmail.com>
On Wed May 20, 2026 at 9:37 AM -03, Ayush Tiwari wrote:
> I reproduced the crash on master. The plan EXPLAIN under
> force_generic_plan shows runtime pruning is in effect:
>
> Update on pt
> Foreign Update on pt_p2 pt_2
> -> Append
> Subplans Removed: 1
> -> Foreign Update on pt_p2 pt_2
>
> The SEGV happens inside postgresBeginForeignModify() because
> ExecInitModifyTable() builds re-indexed "kept" copies of several
> parallel per-result-relation lists after dropping pruned relations -
> withCheckOptionLists, returningLists, updateColnosLists,
> mergeActionLists and mergeJoinConditions, however two members were
> missed:
>
> - node->fdwPrivLists, read with list_nth(node->fdwPrivLists, i) when
> BeginForeignModify() is called, and
> - node->fdwDirectModifyPlans, checked with bms_is_member(i, ...) when
> setting ri_usesFdwDirectModify.
>
> Both were still indexed against the original (pre-pruning) positions
> while the surrounding loop's "i" is now the kept position. When the
> foreign partition's kept-index no longer matched its original index,
> BeginForeignModify() got the wrong fdw_private and crashed.
>
> Attached patch builds re-indexed kept copies for these two arrays in
> the same loop as the other parallel lists, and uses them at the two
> call sites.
>
Hi, thanks for the patch. This issue started on version 18 by commit
cbc127917e0.
The patch fixes the issue and it make sense to me. One a minor comment
is that I think pg_indent is needed on nodeModifyTable.c
--
Matheus Alcantara
EDB: https://www.enterprisedb.com
^ permalink raw reply [nested|flat] 4+ messages in thread
end of thread, other threads:[~2026-05-22 20:56 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-05-18 06:38 BUG #19484: Segmentation fault triggered by FDW PG Bug reporting form <noreply@postgresql.org>
2026-05-20 12:37 ` Ayush Tiwari <ayushtiwari.slg01@gmail.com>
2026-05-20 17:46 ` Etsuro Fujita <etsuro.fujita@gmail.com>
2026-05-22 20:56 ` Matheus Alcantara <matheusssilv97@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