agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
267+ messages / 2 participants
[nested] [flat]

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup.
@ 2024-05-18 01:41  Thomas Munro <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Thomas Munro @ 2024-05-18 01:41 UTC (permalink / raw)

When backend processes were launched in EXEC_BACKEND builds, they would
run LocalProcessControlFile() to read in pg_control and extract several
important settings.

This happens too early to acquire ControlFileLock, and the postmaster is
also not allowed to acquire ControlFileLock, so it can't safely take a
copy to give to the child.

Instead, pass down the "proto-controlfile" that was read by the
postmaster in LocalProcessControlFile().  Introduce functions
ExportProtoControlFile() and ImportProtoControlFile() to allow that.
Subprocesses will extract information from that, and then later attach
to the current control file in shared memory.

Reported-by: Melanie Plageman <[email protected]> per Windows CI failure
Discussion: https://postgr.es/m/CAAKRu_YNGwEYrorQYza_W8tU%2B%3DtoXRHG8HpyHC-KDbZqA_ZVSA%40mail.gmail.com
---
 src/backend/access/transam/xlog.c       | 46 +++++++++++++++++++++++--
 src/backend/postmaster/launch_backend.c | 21 +++++++----
 src/include/access/xlog.h               |  5 +++
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..e52517eb9c1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -575,6 +575,10 @@ static WALInsertLockPadded *WALInsertLocks = NULL;
  */
 static ControlFileData *ControlFile = NULL;
 
+#ifdef EXEC_BACKEND
+static ControlFileData *ProtoControlFile = NULL;
+#endif
+
 /*
  * Calculate the amount of space left on the page after 'endptr'. Beware
  * multiple evaluation!
@@ -692,6 +696,7 @@ static bool PerformRecoveryXLogAction(void);
 static void InitControlFile(uint64 sysidentifier, uint32 data_checksum_version);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
+static void ScanControlFile(void);
 static void UpdateControlFile(void);
 static char *str_time(pg_time_t tnow, char *buf, size_t bufsize);
 
@@ -4385,9 +4390,7 @@ WriteControlFile(void)
 static void
 ReadControlFile(void)
 {
-	pg_crc32c	crc;
 	int			fd;
-	char		wal_segsz_str[20];
 	int			r;
 
 	/*
@@ -4420,6 +4423,15 @@ ReadControlFile(void)
 
 	close(fd);
 
+	ScanControlFile();
+}
+
+static void
+ScanControlFile(void)
+{
+	static char wal_segsz_str[20];
+	pg_crc32c	crc;
+
 	/*
 	 * Check for expected pg_control format version.  If this is wrong, the
 	 * CRC check will likely fail because we'll be checking the wrong number
@@ -4941,8 +4953,33 @@ LocalProcessControlFile(bool reset)
 	Assert(reset || ControlFile == NULL);
 	ControlFile = palloc_object(ControlFileData);
 	ReadControlFile();
+
+#ifdef EXEC_BACKEND
+	/* We need to be able to give this to subprocesses. */
+	ProtoControlFile = ControlFile;
+#endif
 }
 
+#ifdef EXEC_BACKEND
+void
+ExportProtoControlFile(ControlFileData *copy)
+{
+	*copy = *ProtoControlFile;
+}
+
+/*
+ * Like LocalProcessControlFile(), but used early in EXEC_BACKEND children's
+ * startup.  This receives the same file that the postmaster first read.
+ */
+void
+ImportProtoControlFile(const ControlFileData *copy)
+{
+	ControlFile = palloc(sizeof(ControlFileData));
+	*ControlFile = *copy;
+	ScanControlFile();
+}
+#endif
+
 /*
  * Get the wal_level from the control file. For a standby, this value should be
  * considered as its active wal_level, because it may be different from what
@@ -5061,7 +5098,12 @@ XLOGShmemInit(void)
 	if (localControlFile)
 	{
 		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
+#ifdef EXEC_BACKEND
+		/* We still hold a reference to give to subprocesses. */
+		Assert(ProtoControlFile == localControlFile);
+#else
 		pfree(localControlFile);
+#endif
 	}
 
 	/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 45690b11c99..e08a405f949 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -33,6 +33,9 @@
 
 #include <unistd.h>
 
+#include "access/xlog.h"
+#include "catalog/pg_control.h"
+#include "common/file_utils.h"
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -133,6 +136,14 @@ typedef struct
 
 	int			MyPMChildSlot;
 
+	/*
+	 * A copy of the ControlFileData from early in Postmaster startup.  We
+	 * need to access its contents it at a phase of initialization before we
+	 * are allowed to acquire LWLocks, so we can't just use shared memory or
+	 * read the file from disk.
+	 */
+	ControlFileData proto_controlfile;
+
 	/*
 	 * These are only used by backend processes, but are here because passing
 	 * a socket needs some special handling on Windows. 'client_sock' is an
@@ -659,12 +670,6 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	checkDataDir();
 
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
 	/*
 	 * Reload any libraries that were preloaded by the postmaster.  Since we
 	 * exec'd this process, those libraries didn't come along with us; but we
@@ -752,6 +757,8 @@ save_backend_variables(BackendParameters *param,
 	param->MaxBackends = MaxBackends;
 	param->num_pmchild_slots = num_pmchild_slots;
 
+	ExportProtoControlFile(&param->proto_controlfile);
+
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
@@ -1026,6 +1033,8 @@ restore_backend_variables(BackendParameters *param)
 
 	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
 
+	ImportProtoControlFile(&param->proto_controlfile);
+
 	/*
 	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
 	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index fdfb572467b..f1f3ad4e96e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -208,6 +208,7 @@ typedef enum WALAvailability
 
 struct XLogRecData;
 struct XLogReaderState;
+struct ControlFileData;
 
 extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
 								   XLogRecPtr fpw_lsn,
@@ -250,6 +251,10 @@ extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
 extern void LocalProcessControlFile(bool reset);
+#ifdef EXEC_BACKEND
+extern void ExportProtoControlFile(struct ControlFileData *copy);
+extern void ImportProtoControlFile(const struct ControlFileData *copy);
+#endif
 extern WalLevel GetActiveWalLevelOnStandby(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(int code, Datum arg);
-- 
2.47.3


--dhbc6bswyy6qufwn--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread

* [PATCH 2/2] Add the "dummy_table_am" test module
@ 2025-03-01 19:50  Julien Tachoires <[email protected]>
  0 siblings, 0 replies; 267+ messages in thread

From: Julien Tachoires @ 2025-03-01 19:50 UTC (permalink / raw)

This test module is in charge of testing TAM reloptions. It's very
similar to what we do in dummy_index_am as we have to exercise the
exact same kind of feature.
---
 src/test/modules/Makefile                     |   1 +
 src/test/modules/dummy_table_am/Makefile      |  20 +
 src/test/modules/dummy_table_am/README        |  14 +
 .../dummy_table_am/dummy_table_am--1.0.sql    |  13 +
 .../modules/dummy_table_am/dummy_table_am.c   | 588 ++++++++++++++++++
 .../dummy_table_am/dummy_table_am.control     |   5 +
 .../dummy_table_am/expected/reloptions.out    | 181 ++++++
 src/test/modules/dummy_table_am/meson.build   |  33 +
 .../modules/dummy_table_am/sql/reloptions.sql |  99 +++
 src/test/modules/meson.build                  |   1 +
 10 files changed, 955 insertions(+)
 create mode 100644 src/test/modules/dummy_table_am/Makefile
 create mode 100644 src/test/modules/dummy_table_am/README
 create mode 100644 src/test/modules/dummy_table_am/dummy_table_am--1.0.sql
 create mode 100644 src/test/modules/dummy_table_am/dummy_table_am.c
 create mode 100644 src/test/modules/dummy_table_am/dummy_table_am.control
 create mode 100644 src/test/modules/dummy_table_am/expected/reloptions.out
 create mode 100644 src/test/modules/dummy_table_am/meson.build
 create mode 100644 src/test/modules/dummy_table_am/sql/reloptions.sql

diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 4e4be3fa511..8fe2a2904d6 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -9,6 +9,7 @@ SUBDIRS = \
 		  commit_ts \
 		  delay_execution \
 		  dummy_index_am \
+		  dummy_table_am \
 		  dummy_seclabel \
 		  libpq_pipeline \
 		  oauth_validator \
diff --git a/src/test/modules/dummy_table_am/Makefile b/src/test/modules/dummy_table_am/Makefile
new file mode 100644
index 00000000000..94837dff392
--- /dev/null
+++ b/src/test/modules/dummy_table_am/Makefile
@@ -0,0 +1,20 @@
+# src/test/modules/dummy_table_am/Makefile
+
+MODULES = dummy_table_am
+
+EXTENSION = dummy_table_am
+DATA = dummy_table_am--1.0.sql
+PGFILEDESC = "dummy_table_am - table access method template"
+
+REGRESS = reloptions
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/dummy_table_am
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/dummy_table_am/README b/src/test/modules/dummy_table_am/README
new file mode 100644
index 00000000000..50cf08ee3b1
--- /dev/null
+++ b/src/test/modules/dummy_table_am/README
@@ -0,0 +1,14 @@
+Dummy Table AM
+==============
+
+Dummy table AM is a module for testing any facility usable by a table
+access method, whose code is kept a maximum simple.
+
+This includes tests for all relation option types:
+- boolean
+- enum
+- integer
+- real
+- strings (with and without NULL as default)
+
+It also includes tests related to unrecognized options.
diff --git a/src/test/modules/dummy_table_am/dummy_table_am--1.0.sql b/src/test/modules/dummy_table_am/dummy_table_am--1.0.sql
new file mode 100644
index 00000000000..12ad3ad174b
--- /dev/null
+++ b/src/test/modules/dummy_table_am/dummy_table_am--1.0.sql
@@ -0,0 +1,13 @@
+/* src/test/modules/dummy_table_am/dummy_table_am--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION dummy_table_am" to load this file. \quit
+
+CREATE FUNCTION dummy_table_am_handler(internal)
+RETURNS table_am_handler
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+-- Access method
+CREATE ACCESS METHOD dummy_table_am TYPE TABLE HANDLER dummy_table_am_handler;
+COMMENT ON ACCESS METHOD dummy_table_am IS 'Dummy Table Access Method';
diff --git a/src/test/modules/dummy_table_am/dummy_table_am.c b/src/test/modules/dummy_table_am/dummy_table_am.c
new file mode 100644
index 00000000000..a473bc7dd9b
--- /dev/null
+++ b/src/test/modules/dummy_table_am/dummy_table_am.c
@@ -0,0 +1,588 @@
+/*-------------------------------------------------------------------------
+ *
+ * dummy_table_am.c
+ *		Table AM templae main file
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/dummy_table_am/dummy_table_am.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "miscadmin.h"
+
+#include "access/hio.h"
+#include "access/relscan.h"
+#include "access/reloptions.h"
+#include "access/tableam.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+
+PG_MODULE_MAGIC;
+
+/* Base structures for scans */
+typedef struct DummyScanDescData
+{
+	TableScanDescData rs_base;	/* AM independent part of the descriptor */
+
+	/* Add more fields here as needed by the AM. */
+}			DummyScanDescData;
+typedef struct DummyScanDescData *DummyScanDesc;
+
+/* parse table for fillRelOptions */
+static relopt_parse_elt dt_relopt_tab[7];
+
+/* Kind of relation options for dummy index */
+static relopt_kind dt_relopt_kind;
+
+typedef enum DummyAmEnum
+{
+	DUMMY_AM_ENUM_ONE,
+	DUMMY_AM_ENUM_TWO,
+}			DummyAmEnum;
+
+/* Dummy table options */
+typedef struct DummyTableOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			option_int;
+	double		option_real;
+	bool		option_bool;
+	DummyAmEnum option_enum;
+	int			option_string_val_offset;
+	int			option_string_null_offset;
+	int			fillfactor;
+}			DummyTableOptions;
+
+static relopt_enum_elt_def dummyAmEnumValues[] =
+{
+	{"one", DUMMY_AM_ENUM_ONE},
+	{"two", DUMMY_AM_ENUM_TWO},
+	{(const char *) NULL}		/* list terminator */
+};
+
+/* ------------------------------------------------------------------------
+ *                     Dummy Access Method Interface
+ * ------------------------------------------------------------------------
+ */
+
+static const TupleTableSlotOps *
+dummy_slot_callbacks(Relation relation)
+{
+	return &TTSOpsMinimalTuple;
+}
+
+static TableScanDesc
+dummy_scan_begin(Relation relation, Snapshot snapshot, int nkeys, ScanKey key,
+				 ParallelTableScanDesc parallel_scan, uint32 flags)
+{
+	DummyScanDesc scan;
+
+	scan = (DummyScanDesc) palloc(sizeof(DummyScanDescData));
+
+	scan->rs_base.rs_rd = relation;
+	scan->rs_base.rs_snapshot = snapshot;
+	scan->rs_base.rs_nkeys = nkeys;
+	scan->rs_base.rs_flags = flags;
+	scan->rs_base.rs_parallel = parallel_scan;
+
+	return (TableScanDesc) scan;
+}
+
+static void
+dummy_scan_end(TableScanDesc sscan)
+{
+	DummyScanDesc scan = (DummyScanDesc) sscan;
+
+	pfree(scan);
+
+	return;
+}
+
+static void
+dummy_scan_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
+				  bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+	return;
+}
+
+static bool
+dummy_scan_getnextslot(TableScanDesc sscan, ScanDirection direction,
+					   TupleTableSlot *slot)
+{
+	return true;
+}
+
+static void
+dummy_scan_set_tidrange(TableScanDesc sscan, ItemPointer mintid,
+						ItemPointer maxtid)
+{
+	return;
+}
+
+static bool
+dummy_scan_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction,
+								TupleTableSlot *slot)
+{
+	return true;
+}
+
+static Size
+dummy_parallelscan_estimate(Relation rel)
+{
+	return 0;
+}
+
+static Size
+dummy_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan)
+{
+	return 0;
+}
+
+static void
+dummy_parallelscan_reinitialize(Relation rel, ParallelTableScanDesc pscan)
+{
+	return;
+}
+
+static IndexFetchTableData *
+dummy_index_fetch_begin(Relation rel)
+{
+	return NULL;
+}
+
+static void
+dummy_index_fetch_reset(IndexFetchTableData *scan)
+{
+	return;
+}
+
+static void
+dummy_index_fetch_end(IndexFetchTableData *scan)
+{
+	return;
+}
+
+static bool
+dummy_index_fetch_tuple(struct IndexFetchTableData *scan, ItemPointer tid,
+						Snapshot snapshot, TupleTableSlot *slot,
+						bool *call_again, bool *all_dead)
+{
+	return true;
+}
+
+static void
+dummy_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+				   int options, BulkInsertStateData *bistate)
+{
+	DummyTableOptions *relopts;
+
+	relopts = (DummyTableOptions *) relation->rd_options;
+
+	elog(NOTICE, "option_int=%d, option_real=%f, option_bool=%d, option_enum=%d",
+		 relopts->option_int, relopts->option_real, relopts->option_bool, relopts->option_enum);
+
+	return;
+}
+
+static void
+dummy_tuple_insert_speculative(Relation relation, TupleTableSlot *slot,
+							   CommandId cid, int options,
+							   BulkInsertStateData *bistate, uint32 specToken)
+{
+	return;
+}
+
+static void
+dummy_tuple_complete_speculative(Relation relation, TupleTableSlot *slot,
+								 uint32 specToken, bool succeeded)
+{
+	return;
+}
+
+static void
+dummy_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
+				   CommandId cid, int options, BulkInsertStateData *bistate)
+{
+	return;
+}
+
+static TM_Result
+dummy_tuple_delete(Relation relation, ItemPointer tid, CommandId cid,
+				   Snapshot snapshot, Snapshot crosscheck, bool wait,
+				   TM_FailureData *tmfd, bool changingPart)
+{
+	return TM_Ok;
+}
+
+static TM_Result
+dummy_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+				   CommandId cid, Snapshot snapshot, Snapshot crosscheck,
+				   bool wait, TM_FailureData *tmfd,
+				   LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
+{
+	return TM_Ok;
+}
+
+static TM_Result
+dummy_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot,
+				 TupleTableSlot *slot, CommandId cid, LockTupleMode mode,
+				 LockWaitPolicy wait_policy, uint8 flags,
+				 TM_FailureData *tmfd)
+{
+	return TM_Ok;
+}
+
+static bool
+dummy_fetch_row_version(Relation relation, ItemPointer tid,
+						Snapshot snapshot, TupleTableSlot *slot)
+{
+	return false;
+}
+
+static void
+dummy_get_latest_tid(TableScanDesc sscan, ItemPointer tid)
+{
+	return;
+}
+
+static bool
+dummy_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
+{
+	return false;
+}
+
+static bool
+dummy_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot,
+							   Snapshot snapshot)
+{
+	return false;
+}
+
+static TransactionId
+dummy_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
+{
+	return InvalidTransactionId;
+}
+
+static void
+dummy_relation_set_new_filelocator(Relation rel,
+								   const RelFileLocator *newrlocator,
+								   char persistence,
+								   TransactionId *freezeXid,
+								   MultiXactId *minmulti)
+{
+	return;
+}
+
+static void
+dummy_relation_nontransactional_truncate(Relation rel)
+{
+	return;
+}
+
+static void
+dummy_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
+{
+	return;
+}
+
+static void
+dummy_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
+								Relation OldIndex, bool use_sort,
+								TransactionId OldestXmin,
+								TransactionId *xid_cutoff,
+								MultiXactId *multi_cutoff,
+								double *num_tuples,
+								double *tups_vacuumed,
+								double *tups_recently_dead)
+{
+	return;
+}
+
+static void
+dummy_relation_vacuum(Relation rel, struct VacuumParams *params,
+					  BufferAccessStrategy bstrategy)
+{
+	return;
+}
+
+static bool
+dummy_scan_analyze_next_block(TableScanDesc scan, ReadStream *stream)
+{
+	return false;
+}
+
+static bool
+dummy_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
+							  double *liverows, double *deadrows,
+							  TupleTableSlot *slot)
+{
+	return false;
+}
+
+static double
+dummy_index_build_range_scan(Relation heapRelation,
+							 Relation indexRelation,
+							 struct IndexInfo *indexInfo,
+							 bool allow_sync,
+							 bool anyvisible,
+							 bool progress,
+							 BlockNumber start_blockno,
+							 BlockNumber numblocks,
+							 IndexBuildCallback callback,
+							 void *callback_state,
+							 TableScanDesc scan)
+{
+	return 0;
+}
+
+static void
+dummy_index_validate_scan(Relation heapRelation,
+						  Relation indexRelation,
+						  struct IndexInfo *indexInfo,
+						  Snapshot snapshot,
+						  struct ValidateIndexState *state)
+{
+	return;
+}
+
+static uint64
+dummy_relation_size(Relation rel, ForkNumber forkNumber)
+{
+	return 0;
+}
+
+static bool
+dummy_relation_needs_toast_table(Relation rel)
+{
+	return false;
+}
+
+static Oid
+dummy_relation_toast_am(Relation rel)
+{
+	return InvalidOid;
+}
+
+static void
+dummy_relation_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
+								 int32 sliceoffset, int32 slicelength,
+								 struct varlena *result)
+{
+	return;
+}
+
+static void
+dummy_relation_estimate_size(Relation rel, int32 *attr_widths,
+							 BlockNumber *pages, double *tuples,
+							 double *allvisfrac)
+{
+	return;
+}
+
+static bool
+dummy_scan_bitmap_next_block(TableScanDesc scan, BlockNumber *blockno,
+							 bool *recheck, uint64 *lossy_pages,
+							 uint64 *exact_pages)
+{
+	return false;
+}
+
+static bool
+dummy_scan_bitmap_next_tuple(TableScanDesc scan, TupleTableSlot *slot)
+{
+	return false;
+}
+
+static bool
+dummy_scan_sample_next_block(TableScanDesc scan, struct SampleScanState *scanstate)
+{
+	return false;
+}
+
+static bool
+dummy_scan_sample_next_tuple(TableScanDesc scan, struct SampleScanState *scanstate,
+							 TupleTableSlot *slot)
+{
+	return false;
+}
+
+static bytea *
+dummy_relation_options(char relkind, Datum reloptions, bool validate)
+{
+	return (bytea *) build_reloptions(reloptions, validate,
+									  dt_relopt_kind,
+									  sizeof(DummyTableOptions),
+									  dt_relopt_tab, lengthof(dt_relopt_tab));
+}
+
+/*
+ * Validation function for string relation options.
+ */
+static void
+validate_string_option(const char *value)
+{
+	ereport(NOTICE,
+			(errmsg("new option value for string parameter %s",
+					value ? value : "NULL")));
+}
+
+/*
+ * This function creates a full set of relation option types,
+ * with various patterns.
+ */
+static void
+create_reloptions_table(void)
+{
+	dt_relopt_kind = add_reloption_kind();
+
+	add_int_reloption(dt_relopt_kind, "option_int",
+					  "Integer option for dummy_table_am",
+					  10, -10, 100, AccessExclusiveLock);
+	dt_relopt_tab[0].optname = "option_int";
+	dt_relopt_tab[0].opttype = RELOPT_TYPE_INT;
+	dt_relopt_tab[0].offset = offsetof(DummyTableOptions, option_int);
+
+	add_real_reloption(dt_relopt_kind, "option_real",
+					   "Real option for dummy_table_am",
+					   3.1415, -10, 100, AccessExclusiveLock);
+	dt_relopt_tab[1].optname = "option_real";
+	dt_relopt_tab[1].opttype = RELOPT_TYPE_REAL;
+	dt_relopt_tab[1].offset = offsetof(DummyTableOptions, option_real);
+
+	add_bool_reloption(dt_relopt_kind, "option_bool",
+					   "Boolean option for dummy_table_am",
+					   true, AccessExclusiveLock);
+	dt_relopt_tab[2].optname = "option_bool";
+	dt_relopt_tab[2].opttype = RELOPT_TYPE_BOOL;
+	dt_relopt_tab[2].offset = offsetof(DummyTableOptions, option_bool);
+
+	add_enum_reloption(dt_relopt_kind, "option_enum",
+					   "Enum option for dummy_table_am",
+					   dummyAmEnumValues,
+					   DUMMY_AM_ENUM_ONE,
+					   "Valid values are \"one\" and \"two\".",
+					   AccessExclusiveLock);
+	dt_relopt_tab[3].optname = "option_enum";
+	dt_relopt_tab[3].opttype = RELOPT_TYPE_ENUM;
+	dt_relopt_tab[3].offset = offsetof(DummyTableOptions, option_enum);
+
+	add_string_reloption(dt_relopt_kind, "option_string_val",
+						 "String option for dummy_table_am with non-NULL default",
+						 "DefaultValue", &validate_string_option,
+						 AccessExclusiveLock);
+	dt_relopt_tab[4].optname = "option_string_val";
+	dt_relopt_tab[4].opttype = RELOPT_TYPE_STRING;
+	dt_relopt_tab[4].offset = offsetof(DummyTableOptions,
+									   option_string_val_offset);
+
+	/*
+	 * String option for dummy_table_am with NULL default, and without
+	 * description.
+	 */
+	add_string_reloption(dt_relopt_kind, "option_string_null",
+						 NULL,	/* description */
+						 NULL, &validate_string_option,
+						 AccessExclusiveLock);
+	dt_relopt_tab[5].optname = "option_string_null";
+	dt_relopt_tab[5].opttype = RELOPT_TYPE_STRING;
+	dt_relopt_tab[5].offset = offsetof(DummyTableOptions,
+									   option_string_null_offset);
+
+	/*
+	 * fillfactor will be used to check reloption conversion when changing
+	 * table access method between heap AM and dummy_table_am.
+	 */
+	add_int_reloption(dt_relopt_kind, "fillfactor",
+					  "Fillfactor option for dummy_table_am",
+					  10, 0, 90, AccessExclusiveLock);
+	dt_relopt_tab[6].optname = "fillfactor";
+	dt_relopt_tab[6].opttype = RELOPT_TYPE_INT;
+	dt_relopt_tab[6].offset = offsetof(DummyTableOptions, fillfactor);
+}
+
+
+/*
+ * Table Access Method API
+ */
+static const TableAmRoutine dummy_table_am_methods = {
+	.type = T_TableAmRoutine,
+
+	.slot_callbacks = dummy_slot_callbacks,
+	.scan_begin = dummy_scan_begin,
+	.scan_end = dummy_scan_end,
+	.scan_rescan = dummy_scan_rescan,
+	.scan_getnextslot = dummy_scan_getnextslot,
+
+	.scan_set_tidrange = dummy_scan_set_tidrange,
+	.scan_getnextslot_tidrange = dummy_scan_getnextslot_tidrange,
+
+	.parallelscan_estimate = dummy_parallelscan_estimate,
+	.parallelscan_initialize = dummy_parallelscan_initialize,
+	.parallelscan_reinitialize = dummy_parallelscan_reinitialize,
+
+	.index_fetch_begin = dummy_index_fetch_begin,
+	.index_fetch_reset = dummy_index_fetch_reset,
+	.index_fetch_end = dummy_index_fetch_end,
+	.index_fetch_tuple = dummy_index_fetch_tuple,
+
+	.tuple_insert = dummy_tuple_insert,
+	.tuple_insert_speculative = dummy_tuple_insert_speculative,
+	.tuple_complete_speculative = dummy_tuple_complete_speculative,
+	.multi_insert = dummy_multi_insert,
+	.tuple_delete = dummy_tuple_delete,
+	.tuple_update = dummy_tuple_update,
+	.tuple_lock = dummy_tuple_lock,
+
+	.tuple_fetch_row_version = dummy_fetch_row_version,
+	.tuple_get_latest_tid = dummy_get_latest_tid,
+	.tuple_tid_valid = dummy_tuple_tid_valid,
+	.tuple_satisfies_snapshot = dummy_tuple_satisfies_snapshot,
+	.index_delete_tuples = dummy_index_delete_tuples,
+
+	.relation_set_new_filelocator = dummy_relation_set_new_filelocator,
+	.relation_nontransactional_truncate = dummy_relation_nontransactional_truncate,
+	.relation_copy_data = dummy_relation_copy_data,
+	.relation_copy_for_cluster = dummy_relation_copy_for_cluster,
+	.relation_vacuum = dummy_relation_vacuum,
+	.scan_analyze_next_block = dummy_scan_analyze_next_block,
+	.scan_analyze_next_tuple = dummy_scan_analyze_next_tuple,
+	.index_build_range_scan = dummy_index_build_range_scan,
+	.index_validate_scan = dummy_index_validate_scan,
+
+	.relation_size = dummy_relation_size,
+	.relation_needs_toast_table = dummy_relation_needs_toast_table,
+	.relation_toast_am = dummy_relation_toast_am,
+	.relation_fetch_toast_slice = dummy_relation_fetch_toast_slice,
+	.relation_estimate_size = dummy_relation_estimate_size,
+	.relation_options = dummy_relation_options,
+
+	.scan_bitmap_next_block = dummy_scan_bitmap_next_block,
+	.scan_bitmap_next_tuple = dummy_scan_bitmap_next_tuple,
+	.scan_sample_next_block = dummy_scan_sample_next_block,
+	.scan_sample_next_tuple = dummy_scan_sample_next_tuple
+};
+
+PG_FUNCTION_INFO_V1(dummy_table_am_handler);
+
+Datum
+dummy_table_am_handler(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_POINTER(&dummy_table_am_methods);
+}
+
+void
+_PG_init(void)
+{
+	create_reloptions_table();
+}
diff --git a/src/test/modules/dummy_table_am/dummy_table_am.control b/src/test/modules/dummy_table_am/dummy_table_am.control
new file mode 100644
index 00000000000..08f2f868d49
--- /dev/null
+++ b/src/test/modules/dummy_table_am/dummy_table_am.control
@@ -0,0 +1,5 @@
+# dummy_table_am extension
+comment = 'dummy_table_am - table access method template'
+default_version = '1.0'
+module_pathname = '$libdir/dummy_table_am'
+relocatable = true
diff --git a/src/test/modules/dummy_table_am/expected/reloptions.out b/src/test/modules/dummy_table_am/expected/reloptions.out
new file mode 100644
index 00000000000..4c08ac4e3ac
--- /dev/null
+++ b/src/test/modules/dummy_table_am/expected/reloptions.out
@@ -0,0 +1,181 @@
+-- Tests for relation options
+CREATE EXTENSION dummy_table_am;
+CREATE TABLE dummy_test_tab (i int4) USING dummy_table_am;
+-- Silence validation checks for strings
+SET client_min_messages TO 'warning';
+-- Test with default values.
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+ unnest 
+--------
+(0 rows)
+
+DROP TABLE dummy_test_tab;
+-- Test with full set of options.
+-- Allow validation checks for strings
+SET client_min_messages TO 'notice';
+CREATE TABLE dummy_test_tab (i int4)
+  USING dummy_table_am WITH (
+  option_bool = false,
+  option_int = 5,
+  option_real = 3.1,
+  option_enum = 'two',
+  option_string_val = NULL,
+  option_string_null = 'val');
+NOTICE:  new option value for string parameter null
+NOTICE:  new option value for string parameter val
+-- Silence again validation checks for strings until the end of the test.
+SET client_min_messages TO 'warning';
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+         unnest         
+------------------------
+ option_bool=false
+ option_int=5
+ option_real=3.1
+ option_enum=two
+ option_string_val=null
+ option_string_null=val
+(6 rows)
+
+-- ALTER TABLE .. SET
+ALTER TABLE dummy_test_tab SET (option_int = 10);
+ALTER TABLE dummy_test_tab SET (option_bool = true);
+ALTER TABLE dummy_test_tab SET (option_real = 3.2);
+ALTER TABLE dummy_test_tab SET (option_string_val = 'val2');
+ALTER TABLE dummy_test_tab SET (option_string_null = NULL);
+ALTER TABLE dummy_test_tab SET (option_enum = 'one');
+ALTER TABLE dummy_test_tab SET (option_enum = 'three');
+ERROR:  invalid value for enum option "option_enum": three
+DETAIL:  Valid values are "one" and "two".
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+         unnest          
+-------------------------
+ option_int=10
+ option_bool=true
+ option_real=3.2
+ option_string_val=val2
+ option_string_null=null
+ option_enum=one
+(6 rows)
+
+-- ALTER TABLE .. RESET
+ALTER TABLE dummy_test_tab RESET (option_int);
+ALTER TABLE dummy_test_tab RESET (option_bool);
+ALTER TABLE dummy_test_tab RESET (option_real);
+ALTER TABLE dummy_test_tab RESET (option_enum);
+ALTER TABLE dummy_test_tab RESET (option_string_val);
+ALTER TABLE dummy_test_tab RESET (option_string_null);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+ unnest 
+--------
+(0 rows)
+
+-- Cross-type checks for reloption values
+-- Integer
+ALTER TABLE dummy_test_tab SET (option_int = 3.3); -- ok
+ALTER TABLE dummy_test_tab SET (option_int = true); -- error
+ERROR:  invalid value for integer option "option_int": true
+ALTER TABLE dummy_test_tab SET (option_int = 'val3'); -- error
+ERROR:  invalid value for integer option "option_int": val3
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+     unnest     
+----------------
+ option_int=3.3
+(1 row)
+
+ALTER TABLE dummy_test_tab RESET (option_int);
+-- Boolean
+ALTER TABLE dummy_test_tab SET (option_bool = 4); -- error
+ERROR:  invalid value for boolean option "option_bool": 4
+ALTER TABLE dummy_test_tab SET (option_bool = 1); -- ok, as true
+ALTER TABLE dummy_test_tab SET (option_bool = 3.4); -- error
+ERROR:  invalid value for boolean option "option_bool": 3.4
+ALTER TABLE dummy_test_tab SET (option_bool = 'val4'); -- error
+ERROR:  invalid value for boolean option "option_bool": val4
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+    unnest     
+---------------
+ option_bool=1
+(1 row)
+
+ALTER TABLE dummy_test_tab RESET (option_bool);
+-- Float
+ALTER TABLE dummy_test_tab SET (option_real = 4); -- ok
+ALTER TABLE dummy_test_tab SET (option_real = true); -- error
+ERROR:  invalid value for floating point option "option_real": true
+ALTER TABLE dummy_test_tab SET (option_real = 'val5'); -- error
+ERROR:  invalid value for floating point option "option_real": val5
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+    unnest     
+---------------
+ option_real=4
+(1 row)
+
+ALTER TABLE dummy_test_tab RESET (option_real);
+-- Enum
+ALTER TABLE dummy_test_tab SET (option_enum = 'one'); -- ok
+ALTER TABLE dummy_test_tab SET (option_enum = 0); -- error
+ERROR:  invalid value for enum option "option_enum": 0
+DETAIL:  Valid values are "one" and "two".
+ALTER TABLE dummy_test_tab SET (option_enum = true); -- error
+ERROR:  invalid value for enum option "option_enum": true
+DETAIL:  Valid values are "one" and "two".
+ALTER TABLE dummy_test_tab SET (option_enum = 'three'); -- error
+ERROR:  invalid value for enum option "option_enum": three
+DETAIL:  Valid values are "one" and "two".
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+     unnest      
+-----------------
+ option_enum=one
+(1 row)
+
+ALTER TABLE dummy_test_tab RESET (option_enum);
+-- String
+ALTER TABLE dummy_test_tab SET (option_string_val = 4); -- ok
+ALTER TABLE dummy_test_tab SET (option_string_val = 3.5); -- ok
+ALTER TABLE dummy_test_tab SET (option_string_val = true); -- ok, as "true"
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+         unnest         
+------------------------
+ option_string_val=true
+(1 row)
+
+ALTER TABLE dummy_test_tab RESET (option_string_val);
+DROP TABLE dummy_test_tab;
+-- ALTER TABLE SET ACCESS METHOD OPTIONS
+CREATE TABLE heap_tab (i INT4) WITH (fillfactor=100, toast_tuple_target=1000);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'heap_tab';
+         unnest          
+-------------------------
+ fillfactor=100
+ toast_tuple_target=1000
+(2 rows)
+
+-- error: fillfactor is out of bounds: maximum value from the new table am is 90
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am;
+ERROR:  value 100 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "0" and "90".
+-- error: toast_tuple_target does not exist in the new table AM
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (SET fillfactor '50');
+ERROR:  unrecognized parameter "toast_tuple_target"
+-- error: adding is not possible when the parameter is already defined in source reloptions
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (ADD fillfactor '50');
+ERROR:  option "fillfactor" provided more than once
+-- error: the specified option we want to drop does not exist 
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (DROP does_not_exist);
+ERROR:  option "does_not_exist" not found
+-- error: adding unrecognized parameter
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (SET fillfactor '50', DROP toast_tuple_target, ADD unrecognized 'foo');
+ERROR:  unrecognized parameter "unrecognized"
+-- ok
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (DROP fillfactor, DROP toast_tuple_target, option_int '1', option_bool 'true', option_real '0.001', option_enum 'one', option_string_val 'hello');
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'heap_tab';
+         unnest          
+-------------------------
+ option_int=1
+ option_bool=true
+ option_real=0.001
+ option_enum=one
+ option_string_val=hello
+(5 rows)
+
+DROP TABLE heap_tab;
diff --git a/src/test/modules/dummy_table_am/meson.build b/src/test/modules/dummy_table_am/meson.build
new file mode 100644
index 00000000000..6b197b15ffa
--- /dev/null
+++ b/src/test/modules/dummy_table_am/meson.build
@@ -0,0 +1,33 @@
+# Copyright (c) 2022-2025, PostgreSQL Global Development Group
+
+dummy_table_am_sources = files(
+  'dummy_table_am.c',
+)
+
+if host_system == 'windows'
+  dummy_table_am_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'dummy_table_am',
+    '--FILEDESC', 'dummy_table_am - table access method template',])
+endif
+
+dummy_table_am = shared_module('dummy_table_am',
+  dummy_table_am_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += dummy_table_am
+
+test_install_data += files(
+  'dummy_table_am.control',
+  'dummy_table_am--1.0.sql',
+)
+
+tests += {
+  'name': 'dummy_table_am',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'regress': {
+    'sql': [
+      'reloptions',
+    ],
+  },
+}
diff --git a/src/test/modules/dummy_table_am/sql/reloptions.sql b/src/test/modules/dummy_table_am/sql/reloptions.sql
new file mode 100644
index 00000000000..ce02533d42e
--- /dev/null
+++ b/src/test/modules/dummy_table_am/sql/reloptions.sql
@@ -0,0 +1,99 @@
+-- Tests for relation options
+CREATE EXTENSION dummy_table_am;
+
+CREATE TABLE dummy_test_tab (i int4) USING dummy_table_am;
+
+-- Silence validation checks for strings
+SET client_min_messages TO 'warning';
+
+-- Test with default values.
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+DROP TABLE dummy_test_tab;
+
+-- Test with full set of options.
+-- Allow validation checks for strings
+SET client_min_messages TO 'notice';
+CREATE TABLE dummy_test_tab (i int4)
+  USING dummy_table_am WITH (
+  option_bool = false,
+  option_int = 5,
+  option_real = 3.1,
+  option_enum = 'two',
+  option_string_val = NULL,
+  option_string_null = 'val');
+-- Silence again validation checks for strings until the end of the test.
+SET client_min_messages TO 'warning';
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+
+-- ALTER TABLE .. SET
+ALTER TABLE dummy_test_tab SET (option_int = 10);
+ALTER TABLE dummy_test_tab SET (option_bool = true);
+ALTER TABLE dummy_test_tab SET (option_real = 3.2);
+ALTER TABLE dummy_test_tab SET (option_string_val = 'val2');
+ALTER TABLE dummy_test_tab SET (option_string_null = NULL);
+ALTER TABLE dummy_test_tab SET (option_enum = 'one');
+ALTER TABLE dummy_test_tab SET (option_enum = 'three');
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+
+-- ALTER TABLE .. RESET
+ALTER TABLE dummy_test_tab RESET (option_int);
+ALTER TABLE dummy_test_tab RESET (option_bool);
+ALTER TABLE dummy_test_tab RESET (option_real);
+ALTER TABLE dummy_test_tab RESET (option_enum);
+ALTER TABLE dummy_test_tab RESET (option_string_val);
+ALTER TABLE dummy_test_tab RESET (option_string_null);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+
+-- Cross-type checks for reloption values
+-- Integer
+ALTER TABLE dummy_test_tab SET (option_int = 3.3); -- ok
+ALTER TABLE dummy_test_tab SET (option_int = true); -- error
+ALTER TABLE dummy_test_tab SET (option_int = 'val3'); -- error
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+ALTER TABLE dummy_test_tab RESET (option_int);
+-- Boolean
+ALTER TABLE dummy_test_tab SET (option_bool = 4); -- error
+ALTER TABLE dummy_test_tab SET (option_bool = 1); -- ok, as true
+ALTER TABLE dummy_test_tab SET (option_bool = 3.4); -- error
+ALTER TABLE dummy_test_tab SET (option_bool = 'val4'); -- error
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+ALTER TABLE dummy_test_tab RESET (option_bool);
+-- Float
+ALTER TABLE dummy_test_tab SET (option_real = 4); -- ok
+ALTER TABLE dummy_test_tab SET (option_real = true); -- error
+ALTER TABLE dummy_test_tab SET (option_real = 'val5'); -- error
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+ALTER TABLE dummy_test_tab RESET (option_real);
+-- Enum
+ALTER TABLE dummy_test_tab SET (option_enum = 'one'); -- ok
+ALTER TABLE dummy_test_tab SET (option_enum = 0); -- error
+ALTER TABLE dummy_test_tab SET (option_enum = true); -- error
+ALTER TABLE dummy_test_tab SET (option_enum = 'three'); -- error
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+ALTER TABLE dummy_test_tab RESET (option_enum);
+-- String
+ALTER TABLE dummy_test_tab SET (option_string_val = 4); -- ok
+ALTER TABLE dummy_test_tab SET (option_string_val = 3.5); -- ok
+ALTER TABLE dummy_test_tab SET (option_string_val = true); -- ok, as "true"
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_tab';
+ALTER TABLE dummy_test_tab RESET (option_string_val);
+
+DROP TABLE dummy_test_tab;
+
+-- ALTER TABLE SET ACCESS METHOD OPTIONS
+CREATE TABLE heap_tab (i INT4) WITH (fillfactor=100, toast_tuple_target=1000);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'heap_tab';
+-- error: fillfactor is out of bounds: maximum value from the new table am is 90
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am;
+-- error: toast_tuple_target does not exist in the new table AM
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (SET fillfactor '50');
+-- error: adding is not possible when the parameter is already defined in source reloptions
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (ADD fillfactor '50');
+-- error: the specified option we want to drop does not exist 
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (DROP does_not_exist);
+-- error: adding unrecognized parameter
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (SET fillfactor '50', DROP toast_tuple_target, ADD unrecognized 'foo');
+-- ok
+ALTER TABLE heap_tab SET ACCESS METHOD dummy_table_am OPTIONS (DROP fillfactor, DROP toast_tuple_target, option_int '1', option_bool 'true', option_real '0.001', option_enum 'one', option_string_val 'hello');
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'heap_tab';
+DROP TABLE heap_tab;
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 2b057451473..28398254df7 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -4,6 +4,7 @@ subdir('brin')
 subdir('commit_ts')
 subdir('delay_execution')
 subdir('dummy_index_am')
+subdir('dummy_table_am')
 subdir('dummy_seclabel')
 subdir('gin')
 subdir('injection_points')
-- 
2.39.5


--y52row5sua5l45qt--





^ permalink  raw  reply  [nested|flat] 267+ messages in thread


end of thread, other threads:[~2025-03-01 19:50 UTC | newest]

Thread overview: 267+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2024-05-18 01:41 [PATCH v2] Fix pg_control corruption in EXEC_BACKEND startup. Thomas Munro <[email protected]>
2025-03-01 19:50 [PATCH 2/2] Add the "dummy_table_am" test module Julien Tachoires <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox