]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add runtime checks for bogus multixact offsets
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));