From 54068fa3084f7279ce28b1e9c843f716a65a7bb6 Mon Sep 17 00:00:00 2001 From: Zhijie Hou Date: Fri, 30 Oct 2026 12:29:51 +0800 Subject: [PATCH v2] Fix pgoutput crash on missing option values. The replication protocol allows options without values, but pgoutput dereferenced a NULL DefElem argument for proto_version and publication_names. A replication client could therefore crash the walsender and force the postmaster to restart all sessions. Fix this by reading both options with defGetString(), which reports a missing parameter cleanly. Reported-by: Brar Piening Author: Zhijie Hou Reviewed-by: Hayato Kuroda Reviewed-by: Kirill Reshke Discussion: https://postgr.es/m/19795-6d7d506133b7f062@postgresql.org Backpatch-through: 14 --- src/backend/replication/pgoutput/pgoutput.c | 7 ++-- src/bin/pg_basebackup/t/030_pg_recvlogical.pl | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index b481d5bb388..c8caa163c15 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -316,7 +316,7 @@ parse_output_parameters(List *options, PGOutputData *data) { unsigned long parsed; char *endptr; - const char *val = strVal(defel->arg); + const char *val = defGetString(defel); if (protocol_version_given) ereport(ERROR, @@ -334,8 +334,7 @@ parse_output_parameters(List *options, PGOutputData *data) if (parsed > PG_UINT32_MAX) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("proto_version \"%s\" out of range", - strVal(defel->arg)))); + errmsg("proto_version \"%s\" out of range", val))); data->protocol_version = (uint32) parsed; } @@ -351,7 +350,7 @@ parse_output_parameters(List *options, PGOutputData *data) * Pass a copy of the DefElem->arg since SplitIdentifierString * modifies its input. */ - if (!SplitIdentifierString(pstrdup(strVal(defel->arg)), ',', + if (!SplitIdentifierString(pstrdup(defGetString(defel)), ',', &data->publication_names)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), diff --git a/src/bin/pg_basebackup/t/030_pg_recvlogical.pl b/src/bin/pg_basebackup/t/030_pg_recvlogical.pl index 07ad07fef2a..309a6c04044 100644 --- a/src/bin/pg_basebackup/t/030_pg_recvlogical.pl +++ b/src/bin/pg_basebackup/t/030_pg_recvlogical.pl @@ -100,6 +100,41 @@ $node->command_ok( ], 'slot dropped'); +# Check that pgoutput rejects options that require a parameter when passed +# without a value. +$node->safe_psql('postgres', + "SELECT pg_create_logical_replication_slot('pgoutput_slot', 'pgoutput')"); + +$node->command_fails_like( + [ + 'pg_recvlogical', + '--slot' => 'pgoutput_slot', + '--dbname' => $node->connstr('postgres'), + '--start', + '--option' => 'proto_version', + '--no-loop', + '--file' => '-', + ], + qr/proto_version requires a parameter/, + 'pgoutput proto_version option without a value'); + +$node->command_fails_like( + [ + 'pg_recvlogical', + '--slot' => 'pgoutput_slot', + '--dbname' => $node->connstr('postgres'), + '--start', + '--option' => 'proto_version=1', + '--option' => 'publication_names', + '--no-loop', + '--file' => '-', + ], + qr/publication_names requires a parameter/, + 'pgoutput publication_names option without a value'); + +$node->safe_psql('postgres', + "SELECT pg_drop_replication_slot('pgoutput_slot')"); + #test with two-phase option enabled $node->command_ok( [ -- 2.54.0 (Apple Git-157)