From 3e71ce1e9ab73a829ae3ddbcd9b03e6673e39c3e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 29 Jan 2014 20:04:05 -0500
Subject: [PATCH] Fix unsafe references to errno within error messaging logic.

Various places were supposing that errno could be expected to hold still
within an ereport() nest or similar contexts.  This isn't true necessarily,
though in some cases it accidentally failed to fail depending on how the
compiler chanced to order the subexpressions.  This class of thinko
explains recent reports of odd failures on clang-built versions, typically
missing or inappropriate HINT fields in messages.

Problem identified by Christian Kruse, who also submitted the patch this
commit is based on.  (I fixed a few issues in his patch and found a couple
of additional places with the same disease.)

Back-patch as appropriate to all supported branches.
---
 src/backend/commands/tablespace.c |  6 +++++-
 src/backend/port/sysv_sema.c      | 14 ++++++++++----
 src/backend/port/sysv_shmem.c     | 19 +++++++++----------
 3 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index da9cb2f30e9..5fca59b28f2 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -774,10 +774,14 @@ remove_symlink:
 	else
 	{
 		if (unlink(linkloc) < 0)
-			ereport(redo ? LOG : (errno == ENOENT ? WARNING : ERROR),
+		{
+			int			saved_errno = errno;
+
+			ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR),
 					(errcode_for_file_access(),
 					 errmsg("could not remove symbolic link \"%s\": %m",
 							linkloc)));
+		}
 	}
 
 	pfree(linkloc_with_version_dir);
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 08803552ca5..44cba9ea48b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -91,15 +91,17 @@ InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems)
 
 	if (semId < 0)
 	{
+		int			saved_errno = errno;
+
 		/*
 		 * Fail quietly if error indicates a collision with existing set. One
 		 * would expect EEXIST, given that we said IPC_EXCL, but perhaps we
 		 * could get a permission violation instead?  Also, EIDRM might occur
 		 * if an old set is slated for destruction but not gone yet.
 		 */
-		if (errno == EEXIST || errno == EACCES
+		if (saved_errno == EEXIST || saved_errno == EACCES
 #ifdef EIDRM
-			|| errno == EIDRM
+			|| saved_errno == EIDRM
 #endif
 			)
 			return -1;
@@ -112,7 +114,7 @@ InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems)
 				 errdetail("Failed system call was semget(%lu, %d, 0%o).",
 						   (unsigned long) semKey, numSems,
 						   IPC_CREAT | IPC_EXCL | IPCProtection),
-				 (errno == ENOSPC) ?
+				 (saved_errno == ENOSPC) ?
 				 errhint("This error does *not* mean that you have run out of disk space.  "
 		  "It occurs when either the system limit for the maximum number of "
 			 "semaphore sets (SEMMNI), or the system wide maximum number of "
@@ -136,13 +138,17 @@ IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum, int value)
 
 	semun.val = value;
 	if (semctl(semId, semNum, SETVAL, semun) < 0)
+	{
+		int			saved_errno = errno;
+
 		ereport(FATAL,
 				(errmsg_internal("semctl(%d, %d, SETVAL, %d) failed: %m",
 								 semId, semNum, value),
-				 (errno == ERANGE) ?
+				 (saved_errno == ERANGE) ?
 				 errhint("You possibly need to raise your kernel's SEMVMX value to be at least "
 				  "%d.  Look into the PostgreSQL documentation for details.",
 						 value) : 0));
+	}
 }
 
 /*
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index b28766c652e..f280e7023f2 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -76,15 +76,17 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 
 	if (shmid < 0)
 	{
+		int			shmget_errno = errno;
+
 		/*
 		 * Fail quietly if error indicates a collision with existing segment.
 		 * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
 		 * we could get a permission violation instead?  Also, EIDRM might
 		 * occur if an old seg is slated for destruction but not gone yet.
 		 */
-		if (errno == EEXIST || errno == EACCES
+		if (shmget_errno == EEXIST || shmget_errno == EACCES
 #ifdef EIDRM
-			|| errno == EIDRM
+			|| shmget_errno == EIDRM
 #endif
 			)
 			return NULL;
@@ -98,10 +100,8 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 		 * against SHMMIN in the preexisting-segment case, so we will not get
 		 * EINVAL a second time if there is such a segment.
 		 */
-		if (errno == EINVAL)
+		if (shmget_errno == EINVAL)
 		{
-			int			save_errno = errno;
-
 			shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection);
 
 			if (shmid < 0)
@@ -127,8 +127,6 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 					elog(LOG, "shmctl(%d, %d, 0) failed: %m",
 						 (int) shmid, IPC_RMID);
 			}
-
-			errno = save_errno;
 		}
 
 		/*
@@ -140,12 +138,13 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 		 * it should be.  SHMMNI violation is ENOSPC, per spec.  Just plain
 		 * not-enough-RAM is ENOMEM.
 		 */
+		errno = shmget_errno;
 		ereport(FATAL,
 				(errmsg("could not create shared memory segment: %m"),
 		  errdetail("Failed system call was shmget(key=%lu, size=%lu, 0%o).",
 					(unsigned long) memKey, (unsigned long) size,
 					IPC_CREAT | IPC_EXCL | IPCProtection),
-				 (errno == EINVAL) ?
+				 (shmget_errno == EINVAL) ?
 				 errhint("This error usually means that PostgreSQL's request for a shared memory "
 		  "segment exceeded your kernel's SHMMAX parameter.  You can either "
 						 "reduce the request size or reconfigure the kernel with larger SHMMAX.  "
@@ -158,7 +157,7 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 		"The PostgreSQL documentation contains more information about shared "
 						 "memory configuration.",
 						 (unsigned long) size) : 0,
-				 (errno == ENOMEM) ?
+				 (shmget_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request for a shared "
 				   "memory segment exceeded available memory or swap space, "
 			   "or exceeded your kernel's SHMALL parameter.  You can either "
@@ -169,7 +168,7 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 		"The PostgreSQL documentation contains more information about shared "
 						 "memory configuration.",
 						 (unsigned long) size) : 0,
-				 (errno == ENOSPC) ?
+				 (shmget_errno == ENOSPC) ?
 				 errhint("This error does *not* mean that you have run out of disk space.  "
 						 "It occurs either if all available shared memory IDs have been taken, "
 						 "in which case you need to raise the SHMMNI parameter in your kernel, "
-- 
GitLab