public inbox for [email protected]help / color / mirror / Atom feed
BUG #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 <[email protected]> 0 siblings, 1 reply; 4+ messages in thread From: PG Bug reporting form @ 2026-05-18 06:38 UTC (permalink / raw) To: [email protected]; +Cc: [email protected] The following bug has been logged on the website: Bug reference: 19484 Logged by: Chi Zhang Email address: [email protected] 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 <[email protected]> parent: PG Bug reporting form <[email protected]> 0 siblings, 2 replies; 4+ messages in thread From: Ayush Tiwari @ 2026-05-20 12:37 UTC (permalink / raw) To: [email protected]; [email protected]; Etsuro Fujita <[email protected]> Hi, On Wed, 20 May 2026 at 03:59, PG Bug reporting form <[email protected]> wrote: > The following bug has been logged on the website: > > Bug reference: 19484 > Logged by: Chi Zhang > Email address: [email protected] > 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 <[email protected]> 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 <[email protected]> Discussion: https://postgr.es/m/[email protected] --- .../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 <[email protected]> parent: Ayush Tiwari <[email protected]> 1 sibling, 0 replies; 4+ messages in thread From: Etsuro Fujita @ 2026-05-20 17:46 UTC (permalink / raw) To: Ayush Tiwari <[email protected]>; +Cc: [email protected]; [email protected] Hi, On Wed, May 20, 2026 at 5:37 AM Ayush Tiwari <[email protected]> wrote: > On Wed, 20 May 2026 at 03:59, PG Bug reporting form <[email protected]> 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 <[email protected]> parent: Ayush Tiwari <[email protected]> 1 sibling, 0 replies; 4+ messages in thread From: Matheus Alcantara @ 2026-05-22 20:56 UTC (permalink / raw) To: Ayush Tiwari <[email protected]>; [email protected]; [email protected]; Etsuro Fujita <[email protected]> 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 <[email protected]> 2026-05-20 12:37 ` Ayush Tiwari <[email protected]> 2026-05-20 17:46 ` Etsuro Fujita <[email protected]> 2026-05-22 20:56 ` Matheus Alcantara <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox