diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 9568ff1..a12a281 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -348,6 +348,10 @@ static bool MultiXactOffsetPagePrecedes(int page1, int page2);
 static bool MultiXactMemberPagePrecedes(int page1, int page2);
 static bool MultiXactOffsetPrecedes(MultiXactOffset offset1,
 						MultiXactOffset offset2);
+static MultiXactId
+MultiXactIdMostRecent(MultiXactId multi1, MultiXactId multi2);
+static MultiXactId
+find_earliest_multixact_on_disk(void);
 static void ExtendMultiXactOffset(MultiXactId multi);
 static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers);
 static void DetermineSafeOldestOffset(MultiXactId oldestMXact);
@@ -1958,10 +1962,17 @@ StartupMultiXact(void)
 	MultiXactMemberCtl->shared->latest_page_number = pageno;
 
 	/*
-	 * compute the oldest member we need to keep around to avoid old member
-	 * data overrun.
+	 * Compute the oldest member we need to keep around to avoid old member
+	 * data overrun.  We scan the SLRU's physical files to find the earliest
+	 * possible multixact that is stored on disk, and we use that if it's more
+	 * recent than the oldestMultiXactId from shared memory, to avoid
+	 * attempting to accessing a multixact that has already been truncated,
+	 * during a replay (and also to handle a historical pg_upgrade bug that
+	 * might have corrupted oldestMultiXactId in pg_control, leaving it
+	 * pointing to a non-existant multixact).
 	 */
-	DetermineSafeOldestOffset(MultiXactState->oldestMultiXactId);
+	DetermineSafeOldestOffset(MultiXactIdMostRecent(MultiXactState->oldestMultiXactId,
+													find_earliest_multixact_on_disk()));
 }
 
 /*
@@ -2206,7 +2217,10 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
 	 * itself, but there's an important special case: if there are no
 	 * multixacts in existence at all, oldest_datminmxid obviously can't point
 	 * to one.  It will instead point to the multixact ID that will be
-	 * assigned the next time one is needed.
+	 * assigned the next time one is needed.  Note that oldest_datminmxid may
+	 * refer to multixacts that have already been truncated in certain
+	 * circumstances (pg_update corruption caused by a historical bug), so we
+	 * need to use the earliest multixact found on disk if it is more recent.
 	 *
 	 * NB: oldest_dataminmxid is the oldest multixact that might still be
 	 * referenced from a table, unlike in DetermineSafeOldestOffset, where we
@@ -2226,7 +2240,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
 	else
 	{
 		LWLockRelease(MultiXactGenLock);
-		oldestOffset = find_multixact_start(oldest_datminmxid);
+		oldestOffset =
+			find_multixact_start(MultiXactIdMostRecent(oldest_datminmxid,
+													   find_earliest_multixact_on_disk()));
 	}
 
 	/* Grab lock for just long enough to set the new limit values */
@@ -2802,7 +2818,6 @@ TruncateMultiXact(void)
 	MultiXactId oldestMXact;
 	MultiXactOffset oldestOffset;
 	MultiXactOffset nextOffset;
-	mxtruncinfo trunc;
 	MultiXactId earliest;
 	MembersLiveRange range;
 
@@ -2821,11 +2836,7 @@ TruncateMultiXact(void)
 	 * truncate the members SLRU.  So we first scan the directory to determine
 	 * the earliest offsets page number that we can read without error.
 	 */
-	trunc.earliestExistingPage = -1;
-	SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
-	earliest = trunc.earliestExistingPage * MULTIXACT_OFFSETS_PER_PAGE;
-	if (earliest < FirstMultiXactId)
-		earliest = FirstMultiXactId;
+	earliest = find_earliest_multixact_on_disk();
 
 	/* nothing to do */
 	if (MultiXactIdPrecedes(oldestMXact, earliest))
@@ -2869,6 +2880,36 @@ TruncateMultiXact(void)
 }
 
 /*
+ * Returns whichever of its arguments comes 'later'.
+ * Like 'Max', but using Xid-style modulo arithmetic.
+ */
+static MultiXactId
+MultiXactIdMostRecent(MultiXactId multi1, MultiXactId multi2)
+{
+	return MultiXactIdPrecedes(multi1, multi2) ? multi2 : multi1;
+}
+
+/*
+ * Scan the physical files in the offsets SLRU and return the earliest
+ * multixact ID that could be stored there.
+ */
+static MultiXactId
+find_earliest_multixact_on_disk(void)
+{
+	MultiXactId earliest;
+	mxtruncinfo trunc;
+
+	trunc.earliestExistingPage = -1;
+	SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
+	Assert(trunc.earliestExistingPage != -1); /* XXX: error, there must be at least one file */
+	earliest = trunc.earliestExistingPage * MULTIXACT_OFFSETS_PER_PAGE;
+	if (earliest < FirstMultiXactId)
+		earliest = FirstMultiXactId;
+
+	return earliest;
+}
+
+/*
  * Decide which of two MultiXactOffset page numbers is "older" for truncation
  * purposes.
  *
