From d4b7bde4183bc39c6d7a22d22188fc4aab2ba8d1 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 11 Dec 2025 11:18:14 +0200 Subject: [PATCH] Add runtime checks for bogus multixact offsets It's not far-fetched that we'd try to read a multixid with an invalid offset in case of bugs or corruption. Or if you call pg_get_multixact_members() after a crash that left behind invalid but unused multixids. Better to get a somewhat descriptive error message if that happens. Discussion: https://www.postgresql.org/message-id/3624730d-6dae-42bf-9458-76c4c965fb27@iki.fi --- src/backend/access/transam/multixact.c | 33 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 3a95f9ca1f8..8ba2f4529dc 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -1153,6 +1153,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, int slotno; MultiXactOffset *offptr; MultiXactOffset offset; + MultiXactOffset nextMXOffset; int length; MultiXactId oldestMXact; MultiXactId nextMXact; @@ -1244,12 +1245,14 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, offptr += entryno; offset = *offptr; - Assert(offset != 0); + if (offset == 0) + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg("MultiXact %u has invalid offset", multi))); /* read next multi's offset */ { MultiXactId tmpMXact; - MultiXactOffset nextMXOffset; /* handle wraparound if needed */ tmpMXact = multi + 1; @@ -1283,21 +1286,27 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno]; offptr += entryno; nextMXOffset = *offptr; - - if (nextMXOffset == 0) - ereport(ERROR, - (errcode(ERRCODE_DATA_CORRUPTED), - errmsg("MultiXact %u has invalid next offset", - multi))); - - length = nextMXOffset - offset; } LWLockRelease(lock); lock = NULL; - /* A multixid with zero members should not happen */ - Assert(length > 0); + /* Sanity check the next offset */ + if (nextMXOffset == 0) + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg("MultiXact %u has invalid next offset", multi))); + if (nextMXOffset < offset) + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg("MultiXact %u has offset (%" PRIu64 ") greater than its next offset (%" PRIu64 ")", + multi, offset, nextMXOffset))); + if (nextMXOffset - offset > INT32_MAX) + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg("MultiXact %u has too many members (%" PRIu64 ")", + multi, nextMXOffset - offset))); + length = nextMXOffset - offset; /* read the members */ ptr = (MultiXactMember *) palloc(length * sizeof(MultiXactMember)); -- 2.39.5