v1-0000-head-invalidate-pgxactoff-after-remove-pgproc.patch

application/octet-stream

Filename: v1-0000-head-invalidate-pgxactoff-after-remove-pgproc.patch
Type: application/octet-stream
Part: 0
Message: ReplicationSlotRelease may set the statusFlags of other processes in PG14

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: unified
Series: patch v1-0000
File+
src/backend/commands/indexcmds.c 1 0
src/backend/commands/vacuum.c 1 0
src/backend/replication/logical/logical.c 1 0
src/backend/replication/slot.c 1 0
src/backend/replication/walsender.c 1 0
src/backend/storage/ipc/procarray.c 4 0
src/backend/storage/lmgr/proc.c 4 0
src/include/storage/proc.h 6 0
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index de89be8d75..b0eb267971 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4563,6 +4563,7 @@ set_indexsafe_procflags(void)
 
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 	MyProc->statusFlags |= PROC_IN_SAFE_IC;
+	Assert(PGXactOffIsValid(MyProc->pgxactoff));
 	ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
 	LWLockRelease(ProcArrayLock);
 }
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index e63c86cae4..8b79c6b6fd 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1991,6 +1991,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params,
 		MyProc->statusFlags |= PROC_IN_VACUUM;
 		if (params->is_wraparound)
 			MyProc->statusFlags |= PROC_VACUUM_FOR_WRAPAROUND;
+		Assert(PGXactOffIsValid(MyProc->pgxactoff));
 		ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
 		LWLockRelease(ProcArrayLock);
 	}
diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c
index 51ffb623c0..34b3bdd61c 100644
--- a/src/backend/replication/logical/logical.c
+++ b/src/backend/replication/logical/logical.c
@@ -195,6 +195,7 @@ StartupDecodingContext(List *output_plugin_options,
 	{
 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 		MyProc->statusFlags |= PROC_IN_LOGICAL_DECODING;
+		Assert(PGXactOffIsValid(MyProc->pgxactoff));
 		ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
 		LWLockRelease(ProcArrayLock);
 	}
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index 91ca397857..73c8ec7f62 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -696,6 +696,7 @@ ReplicationSlotRelease(void)
 	/* might not have been set when we've been a plain slot */
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 	MyProc->statusFlags &= ~PROC_IN_LOGICAL_DECODING;
+	Assert(PGXactOffIsValid(MyProc->pgxactoff));
 	ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
 	LWLockRelease(ProcArrayLock);
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index bc40c454de..cfb2fd6cd4 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -308,6 +308,7 @@ InitWalSender(void)
 		Assert(MyProc->xmin == InvalidTransactionId);
 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 		MyProc->statusFlags |= PROC_AFFECTS_ALL_HORIZONS;
+		Assert(PGXactOffIsValid(MyProc->pgxactoff));
 		ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
 		LWLockRelease(ProcArrayLock);
 	}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index b3cd248fb6..b8201418ed 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -608,6 +608,7 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
 	Assert(ProcGlobal->subxidStates[myoff].overflowed == false);
 
 	ProcGlobal->statusFlags[myoff] = 0;
+	proc->pgxactoff = InvalidPGXactOff;
 
 	/* Keep the PGPROC array sorted. See notes above */
 	movecount = arrayP->numProcs - myoff - 1;
@@ -715,6 +716,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 			Assert(!LWLockHeldByMe(ProcArrayLock));
 			LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 			Assert(proc->statusFlags == ProcGlobal->statusFlags[proc->pgxactoff]);
+			Assert(PGXactOffIsValid(MyProc->pgxactoff));
 			proc->statusFlags &= ~PROC_VACUUM_STATE_MASK;
 			ProcGlobal->statusFlags[proc->pgxactoff] = proc->statusFlags;
 			LWLockRelease(ProcArrayLock);
@@ -755,6 +757,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
 	if (proc->statusFlags & PROC_VACUUM_STATE_MASK)
 	{
 		proc->statusFlags &= ~PROC_VACUUM_STATE_MASK;
+		Assert(PGXactOffIsValid(MyProc->pgxactoff));
 		ProcGlobal->statusFlags[proc->pgxactoff] = proc->statusFlags;
 	}
 
@@ -2636,6 +2639,7 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc)
 		MyProc->xmin = TransactionXmin = xmin;
 		MyProc->statusFlags = (MyProc->statusFlags & ~PROC_XMIN_FLAGS) |
 			(proc->statusFlags & PROC_XMIN_FLAGS);
+		Assert(PGXactOffIsValid(MyProc->pgxactoff));
 		ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
 
 		result = true;
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 162b1f919d..1941618aa3 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -192,6 +192,10 @@ InitProcGlobal(void)
 	procs = (PGPROC *) ShmemAlloc(TotalProcs * sizeof(PGPROC));
 	MemSet(procs, 0, TotalProcs * sizeof(PGPROC));
 	ProcGlobal->allProcs = procs;
+	for (int index = 0; index < TotalProcs; index++)
+	{
+		procs[index].pgxactoff = InvalidPGXactOff;
+	}
 	/* XXX allProcCount isn't really all of them; it excludes prepared xacts */
 	ProcGlobal->allProcCount = MaxBackends + NUM_AUXILIARY_PROCS;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 18891a86fb..72ca279f78 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -127,6 +127,12 @@ typedef enum
 	PROC_WAIT_STATUS_ERROR,
 } ProcWaitStatus;
 
+/*
+ * Invalid value for PGPROC->pgxactoff.
+ */
+#define InvalidPGXactOff -1
+#define PGXactOffIsValid(pgxactoff)		((pgxactoff) != InvalidPGXactOff)
+
 /*
  * Each backend has a PGPROC struct in shared memory.  There is also a list of
  * currently-unused PGPROC structs that will be reallocated to new backends.