0001-Replace-debug-only-Asserts-with-runtime-checks-in-lo.patch
application/octet-stream
Filename: 0001-Replace-debug-only-Asserts-with-runtime-checks-in-lo.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0001
Subject: Replace debug-only Asserts with runtime checks in logical replication apply worker
| File | + | − |
|---|---|---|
| src/backend/replication/logical/worker.c | 19 | 4 |
From f7695baed40e5bb206d7e42910eaf0348b7fdbac Mon Sep 17 00:00:00 2001
From: Varik Matevosyan <varikmatevosyan@gmail.com>
Date: Mon, 4 May 2026 14:33:09 +0000
Subject: [PATCH] Replace debug-only Asserts with runtime checks in logical
replication apply worker
Three locations in worker.c use Assert() to guard against a mismatch
between the number of columns advertised in the RELATION message and
the number actually received in the subsequent INSERT/UPDATE tuple
message. Since these values originate from the publisher, the check
must survive into production builds.
A malicious or buggy publisher can send a RELATION claiming N columns
and an INSERT claiming M < N columns. The subscriber's apply worker
indexes into colvalues[]/colstatus[] using column indices from the
RELATION message's attribute map, causing a heap out-of-bounds read
when the tuple's column array is smaller than expected.
Replace the Asserts with ereport(ERROR, ERRCODE_PROTOCOL_VIOLATION)
that produces a clear diagnostic and cleanly aborts the transaction.
---
src/backend/replication/logical/worker.c | 23 +++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index dd6fc38a41e..a3f2406ed83 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -1038,9 +1038,15 @@ slot_store_data(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
if (!att->attisdropped && remoteattnum >= 0)
{
- StringInfo colvalue = &tupleData->colvalues[remoteattnum];
+ StringInfo colvalue;
+
+ if (remoteattnum >= tupleData->ncols)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("logical replication column %d not found in tuple: only %d column(s) received",
+ remoteattnum + 1, tupleData->ncols)));
- Assert(remoteattnum < tupleData->ncols);
+ colvalue = &tupleData->colvalues[remoteattnum];
/* Set attnum for error callback */
apply_error_callback_arg.remote_attnum = remoteattnum;
@@ -1151,7 +1157,11 @@ slot_modify_data(TupleTableSlot *slot, TupleTableSlot *srcslot,
if (remoteattnum < 0)
continue;
- Assert(remoteattnum < tupleData->ncols);
+ if (remoteattnum >= tupleData->ncols)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("logical replication column %d not found in tuple: only %d column(s) received",
+ remoteattnum + 1, tupleData->ncols)));
if (tupleData->colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED)
{
@@ -2870,7 +2880,12 @@ apply_handle_update(StringInfo s)
if (!att->attisdropped && remoteattnum >= 0)
{
- Assert(remoteattnum < newtup.ncols);
+ if (remoteattnum >= newtup.ncols)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("logical replication column %d not found in tuple: only %d column(s) received",
+ remoteattnum + 1, newtup.ncols)));
+
if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED)
target_perminfo->updatedCols =
bms_add_member(target_perminfo->updatedCols,
--
2.43.0