v1-0000-v15-v16-invalidate-pgxactoff-after-remove-pgproc.patch

application/octet-stream

Filename: v1-0000-v15-v16-invalidate-pgxactoff-after-remove-pgproc.patch
Type: application/octet-stream
Part: 3
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 e4c792228f..85d10ba338 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4397,6 +4397,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 69ac276687..d4b095ed0e 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2011,6 +2011,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 41243d0187..1dd47e158a 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 e70f066eb8..c1a46df56d 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -593,6 +593,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 4c53de08b9..2e670f6d0f 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -296,6 +296,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 e630302351..3ec2d19cd3 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -611,6 +611,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;
@@ -718,6 +719,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);
@@ -758,6 +760,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;
 	}
 
@@ -2655,6 +2658,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 e9e445bb21..2d33f43d50 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 ef74f32693..c137ce228d 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -132,6 +132,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.