From d0c7cf31a72278dceab554fb3ba60ff84cf7d8f0 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Fri, 24 Jul 2026 11:22:41 +0800
Subject: [PATCH v1] Protect PGPROC lookup when terminating background workers

TerminateBackgroundWorkersForDatabase() used BackendPidGetProc()
and accessed the returned PGPROC after ProcArrayLock had been
released. The PGPROC could be recycled in the meantime, causing
the database ID of another process to be examined.

Hold ProcArrayLock while looking up the process with
BackendPidGetProcWithLock() and checking its database ID. Also
save the worker PID locally so that the lookup and log message
use the same value if the postmaster updates the background
worker slot concurrently.

This issue was introduced by f1e251be8.

Author: Chao Li <lic@highgo.com>
---
 src/backend/postmaster/bgworker.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index f2cffce3ff6..230cec47818 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -1442,16 +1442,24 @@ TerminateBackgroundWorkersForDatabase(Oid databaseId)
 		if (slot->in_use &&
 			(slot->worker.bgw_flags & BGWORKER_INTERRUPTIBLE))
 		{
-			PGPROC	   *proc = BackendPidGetProc(slot->pid);
+			PGPROC	   *proc;
+			pid_t		pid;
+			bool		terminate = false;
 
+			LWLockAcquire(ProcArrayLock, LW_SHARED);
+			pid = slot->pid;
+			proc = BackendPidGetProcWithLock(pid);
 			if (proc && proc->databaseId == databaseId)
 			{
 				slot->terminate = true;
 				signal_postmaster = true;
+				terminate = true;
+			}
+			LWLockRelease(ProcArrayLock);
 
+			if (terminate)
 				elog(DEBUG1, "termination requested for worker (PID %d) on database %u",
-					 (int) slot->pid, databaseId);
-			}
+					 (int) pid, databaseId);
 		}
 	}
 
-- 
2.50.1 (Apple Git-155)

