Add runtime checks for bogus multixact offsets master github/master
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 11 Dec 2025 09:18:14 +0000 (11:18 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 11 Dec 2025 09:18:14 +0000 (11:18 +0200)
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

index 3a95f9ca1f8313a3aa597f8cbabc36f96a0ee997..8ba2f4529dc19bb928dc8a1fcc1b5196818179dc 100644 (file)
@@ -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));