From 104a8f2f1bfe791b625752e25479b35b8f6bed23 Mon Sep 17 00:00:00 2001
From: Ian Ilyasov <ianilyasov@outlook.com>
Date: Tue, 29 Oct 2024 17:59:29 +0300
Subject: [PATCH] fix possible SIGSEGV situations on rubbish
 pg_control

uint32 for WalSegSz is crucial as xlog_seg_size in pg_control.h
is also uint32 and there could be situation when WalSegSz gets
a negative value and pg_controldata triggers FPE (division by zero):

((0x100000000UL) / (WalSegSz)) can turn into zero in

XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID,
				 segno, WalSegSz);

because if WalSegSz is -1 then by arithmetic rules in C we get
0x100000000UL / 0xFFFFFFFFFFFFFFFFUL == 0.
---
 src/bin/pg_controldata/pg_controldata.c | 30 +++++++++++++++++++------
 src/include/access/xlog_internal.h      |  2 +-
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 93a05d80ca7..bf759c050d5 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -97,6 +97,7 @@ main(int argc, char *argv[])
 	bool		crc_ok;
 	char	   *DataDir = NULL;
 	time_t		time_tmp;
+	struct tm  *tm_tmp;
 	char		pgctime_str[128];
 	char		ckpttime_str[128];
 	char		mock_auth_nonce_str[MOCK_AUTH_NONCE_LEN * 2 + 1];
@@ -105,7 +106,7 @@ main(int argc, char *argv[])
 	char		xlogfilename[MAXFNAMELEN];
 	int			c;
 	int			i;
-	int			WalSegSz;
+	uint32			WalSegSz;
 
 	pg_logging_init(argv[0]);
 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_controldata"));
@@ -178,8 +179,8 @@ main(int argc, char *argv[])
 
 	if (!IsValidWalSegSize(WalSegSz))
 	{
-		pg_log_warning(ngettext("invalid WAL segment size in control file (%d byte)",
-								"invalid WAL segment size in control file (%d bytes)",
+		pg_log_warning(ngettext("invalid WAL segment size in control file (%u byte)",
+								"invalid WAL segment size in control file (%u bytes)",
 								WalSegSz),
 					   WalSegSz);
 		pg_log_warning_detail("The WAL segment size must be a power of two between 1 MB and 1 GB.");
@@ -196,11 +197,26 @@ main(int argc, char *argv[])
 	 * about %c
 	 */
 	time_tmp = (time_t) ControlFile->time;
-	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
-			 localtime(&time_tmp));
+	tm_tmp = localtime(&time_tmp);
+
+	if (tm_tmp != NULL)
+	{
+		strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
+			 tm_tmp);
+	} else {
+		snprintf(pgctime_str, sizeof(pgctime_str), "(corrupted timestamp)");
+	}
+
 	time_tmp = (time_t) ControlFile->checkPointCopy.time;
-	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
-			 localtime(&time_tmp));
+	tm_tmp = localtime(&time_tmp);
+
+	if (tm_tmp != NULL)
+	{
+		strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
+			 tm_tmp);
+	} else {
+		snprintf(ckpttime_str, sizeof(ckpttime_str), "(corrupted timestamp)");
+	}
 
 	/*
 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index c6a91fb4560..5ecbbba71dd 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -163,7 +163,7 @@ typedef XLogLongPageHeaderData *XLogLongPageHeader;
  * function allocating the result generated.
  */
 static inline void
-XLogFileName(char *fname, TimeLineID tli, XLogSegNo logSegNo, int wal_segsz_bytes)
+XLogFileName(char *fname, TimeLineID tli, XLogSegNo logSegNo, unsigned int wal_segsz_bytes)
 {
 	snprintf(fname, MAXFNAMELEN, "%08X%08X%08X", tli,
 			 (uint32) (logSegNo / XLogSegmentsPerXLogId(wal_segsz_bytes)),
-- 
2.39.5

