summaryrefslogtreecommitdiff
path: root/src/backend/port
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2019-09-05 13:31:41 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2019-09-05 13:31:46 -0400
commit7de19fbc0b1a9172d0907017302b32846b2887b9 (patch)
tree2480c024d6929260b8dd6a324af5b2b5c0014574 /src/backend/port
parent8b94dab06617ef80a0901ab103ebd8754427ef5a (diff)
downloadpostgresql-7de19fbc0b1a9172d0907017302b32846b2887b9.tar.gz
Use data directory inode number, not port, to select SysV resource keys.
This approach provides a much tighter binding between a data directory and the associated SysV shared memory block (and SysV or named-POSIX semaphores, if we're using those). Key collisions are still possible, but only between data directories stored on different filesystems, so the situation should be negligible in practice. More importantly, restarting the postmaster with a different port number no longer risks failing to identify a relevant shared memory block, even when postmaster.pid has been removed. A standalone backend is likewise much more certain to detect conflicting leftover backends. (In the longer term, we might now think about deprecating the port as a cluster-wide value, so that one postmaster could support sockets with varying port numbers. But that's for another day.) The hazards fixed here apply only on Unix systems; our Windows code paths already use identifiers derived from the data directory path name rather than the port. src/test/recovery/t/017_shm.pl, which intends to test key-collision cases, has been substantially rewritten since it can no longer use two postmasters with identical port numbers to trigger the case. Instead, use Perl's IPC::SharedMem module to create a conflicting shmem segment directly. The test script will be skipped if that module is not available. (This means that some older buildfarm members won't run it, but I don't think that that results in any meaningful coverage loss.) Patch by me; thanks to Noah Misch and Peter Eisentraut for discussion and review. Discussion: https://postgr.es/m/16908.1557521200@sss.pgh.pa.us
Diffstat (limited to 'src/backend/port')
-rw-r--r--src/backend/port/posix_sema.c23
-rw-r--r--src/backend/port/sysv_sema.c23
-rw-r--r--src/backend/port/sysv_shmem.c38
-rw-r--r--src/backend/port/win32_sema.c2
-rw-r--r--src/backend/port/win32_shmem.c2
5 files changed, 57 insertions, 31 deletions
diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 3370adf35e..bdd6552f61 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -29,6 +29,7 @@
#include <semaphore.h>
#include <signal.h>
#include <unistd.h>
+#include <sys/stat.h>
#include "miscadmin.h"
#include "storage/ipc.h"
@@ -181,10 +182,6 @@ PGSemaphoreShmemSize(int maxSemas)
* are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
* callback to release them.
*
- * The port number is passed for possible use as a key (for Posix, we use
- * it to generate the starting semaphore name). In a standalone backend,
- * zero will be passed.
- *
* In the Posix implementation, we acquire semaphores on-demand; the
* maxSemas parameter is just used to size the arrays. For unnamed
* semaphores, there is an array of PGSemaphoreData structs in shared memory.
@@ -196,8 +193,22 @@ PGSemaphoreShmemSize(int maxSemas)
* we don't have to expose the counters to other processes.)
*/
void
-PGReserveSemaphores(int maxSemas, int port)
+PGReserveSemaphores(int maxSemas)
{
+ struct stat statbuf;
+
+ /*
+ * We use the data directory's inode number to seed the search for free
+ * semaphore keys. This minimizes the odds of collision with other
+ * postmasters, while maximizing the odds that we will detect and clean up
+ * semaphores left over from a crashed postmaster in our own directory.
+ */
+ if (stat(DataDir, &statbuf) < 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not stat data directory \"%s\": %m",
+ DataDir)));
+
#ifdef USE_NAMED_POSIX_SEMAPHORES
mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *));
if (mySemPointers == NULL)
@@ -215,7 +226,7 @@ PGReserveSemaphores(int maxSemas, int port)
numSems = 0;
maxSems = maxSemas;
- nextSemKey = port * 1000;
+ nextSemKey = statbuf.st_ino;
on_shmem_exit(ReleaseSemaphores, 0);
}
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index ac5106f39d..a1652cce59 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -17,6 +17,7 @@
#include <signal.h>
#include <unistd.h>
#include <sys/file.h>
+#include <sys/stat.h>
#ifdef HAVE_SYS_IPC_H
#include <sys/ipc.h>
#endif
@@ -301,10 +302,6 @@ PGSemaphoreShmemSize(int maxSemas)
* are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
* callback to release them.
*
- * The port number is passed for possible use as a key (for SysV, we use
- * it to generate the starting semaphore key). In a standalone backend,
- * zero will be passed.
- *
* In the SysV implementation, we acquire semaphore sets on-demand; the
* maxSemas parameter is just used to size the arrays. There is an array
* of PGSemaphoreData structs in shared memory, and a postmaster-local array
@@ -314,8 +311,22 @@ PGSemaphoreShmemSize(int maxSemas)
* have clobbered.)
*/
void
-PGReserveSemaphores(int maxSemas, int port)
+PGReserveSemaphores(int maxSemas)
{
+ struct stat statbuf;
+
+ /*
+ * We use the data directory's inode number to seed the search for free
+ * semaphore keys. This minimizes the odds of collision with other
+ * postmasters, while maximizing the odds that we will detect and clean up
+ * semaphores left over from a crashed postmaster in our own directory.
+ */
+ if (stat(DataDir, &statbuf) < 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not stat data directory \"%s\": %m",
+ DataDir)));
+
/*
* We must use ShmemAllocUnlocked(), since the spinlock protecting
* ShmemAlloc() won't be ready yet. (This ordering is necessary when we
@@ -332,7 +343,7 @@ PGReserveSemaphores(int maxSemas, int port)
if (mySemaSets == NULL)
elog(PANIC, "out of memory");
numSemaSets = 0;
- nextSemaKey = port * 1000;
+ nextSemaKey = statbuf.st_ino;
nextSemaNumber = SEMAS_PER_SET; /* force sema set alloc on 1st call */
on_shmem_exit(ReleaseSemaphores, 0);
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 968506dd51..dab2920ad6 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -390,11 +390,12 @@ PGSharedMemoryAttach(IpcMemoryId shmId,
/*
* Try to attach to the segment and see if it matches our data directory.
- * This avoids key-conflict problems on machines that are running several
- * postmasters under the same userid and port number. (That would not
- * ordinarily happen in production, but it can happen during parallel
- * testing. Since our test setups don't open any TCP ports on Unix, such
- * cases don't conflict otherwise.)
+ * This avoids any risk of duplicate-shmem-key conflicts on machines that
+ * are running several postmasters under the same userid.
+ *
+ * (When we're called from PGSharedMemoryCreate, this stat call is
+ * duplicative; but since this isn't a high-traffic case it's not worth
+ * trying to optimize.)
*/
if (stat(DataDir, &statbuf) < 0)
return SHMSTATE_ANALYSIS_FAILURE; /* can't stat; be conservative */
@@ -617,12 +618,9 @@ AnonymousShmemDetach(int status, Datum arg)
* we do not fail upon collision with foreign shmem segments. The idea here
* is to detect and re-use keys that may have been assigned by a crashed
* postmaster or backend.
- *
- * The port number is passed for possible use as a key (for SysV, we use
- * it to generate the starting shmem key).
*/
PGShmemHeader *
-PGSharedMemoryCreate(Size size, int port,
+PGSharedMemoryCreate(Size size,
PGShmemHeader **shim)
{
IpcMemoryKey NextShmemSegID;
@@ -631,6 +629,17 @@ PGSharedMemoryCreate(Size size, int port,
struct stat statbuf;
Size sysvsize;
+ /*
+ * We use the data directory's ID info (inode and device numbers) to
+ * positively identify shmem segments associated with this data dir, and
+ * also as seeds for searching for a free shmem key.
+ */
+ if (stat(DataDir, &statbuf) < 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not stat data directory \"%s\": %m",
+ DataDir)));
+
/* Complain if hugepages demanded but we can't possibly support them */
#if !defined(MAP_HUGETLB)
if (huge_pages == HUGE_PAGES_ON)
@@ -659,10 +668,10 @@ PGSharedMemoryCreate(Size size, int port,
/*
* Loop till we find a free IPC key. Trust CreateDataDirLockFile() to
* ensure no more than one postmaster per data directory can enter this
- * loop simultaneously. (CreateDataDirLockFile() does not ensure that,
- * but prefer fixing it over coping here.)
+ * loop simultaneously. (CreateDataDirLockFile() does not entirely ensure
+ * that, but prefer fixing it over coping here.)
*/
- NextShmemSegID = 1 + port * 1000;
+ NextShmemSegID = statbuf.st_ino;
for (;;)
{
@@ -748,11 +757,6 @@ PGSharedMemoryCreate(Size size, int port,
hdr->dsm_control = 0;
/* Fill in the data directory ID info, too */
- if (stat(DataDir, &statbuf) < 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not stat data directory \"%s\": %m",
- DataDir)));
hdr->device = statbuf.st_dev;
hdr->inode = statbuf.st_ino;
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 013c122451..32cc697866 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
* process exits.
*/
void
-PGReserveSemaphores(int maxSemas, int port)
+PGReserveSemaphores(int maxSemas)
{
mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
if (mySemSet == NULL)
diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index ccd7b6b5e3..6cb6328284 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -194,7 +194,7 @@ EnableLockPagesPrivilege(int elevel)
* standard header.
*/
PGShmemHeader *
-PGSharedMemoryCreate(Size size, int port,
+PGSharedMemoryCreate(Size size,
PGShmemHeader **shim)
{
void *memAddress;