Skip to content

Commit 8f6278d

Browse files
committed
Put in place some defenses against being fooled by accidental match of
shared memory segment ID. If we can't access the existing shmem segment, it must not be relevant to our data directory. If we can access it, then attach to it and check for an actual match to the data directory. This should avoid some cases of failure-to-restart-after-boot without introducing any significant risk of failing to detect a still-running old backend.
1 parent 58825b8 commit 8f6278d

File tree

2 files changed

+91
-29
lines changed

2 files changed

+91
-29
lines changed

src/backend/port/sysv_shmem.c

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* Portions Copyright (c) 1994, Regents of the University of California
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.39 2004/10/13 01:25:11 neilc Exp $
13+
* $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.40 2004/11/09 21:30:13 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -20,6 +20,7 @@
2020
#include <signal.h>
2121
#include <unistd.h>
2222
#include <sys/file.h>
23+
#include <sys/stat.h>
2324
#ifdef HAVE_SYS_IPC_H
2425
#include <sys/ipc.h>
2526
#endif
@@ -40,6 +41,12 @@ typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */
4041

4142
#define IPCProtection (0600) /* access/modify by user only */
4243

44+
#ifdef SHM_SHARE_MMU /* use intimate shared memory on Solaris */
45+
#define PG_SHMAT_FLAGS SHM_SHARE_MMU
46+
#else
47+
#define PG_SHMAT_FLAGS 0
48+
#endif
49+
4350

4451
unsigned long UsedShmemSegID = 0;
4552
void *UsedShmemSegAddr = NULL;
@@ -135,16 +142,10 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size)
135142
on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
136143

137144
/* OK, should be able to attach to the segment */
138-
#ifdef SHM_SHARE_MMU
139-
/* use intimate shared memory on Solaris */
140-
memAddress = shmat(shmid, 0, SHM_SHARE_MMU);
141-
#else
142-
143145
#ifdef EXEC_BACKEND
144-
memAddress = shmat(shmid, UsedShmemSegAddr, 0);
146+
memAddress = shmat(shmid, UsedShmemSegAddr, PG_SHMAT_FLAGS);
145147
#else
146-
memAddress = shmat(shmid, NULL, 0);
147-
#endif
148+
memAddress = shmat(shmid, NULL, PG_SHMAT_FLAGS);
148149
#endif
149150

150151
if (memAddress == (void *) -1)
@@ -188,20 +189,26 @@ IpcMemoryDelete(int status, Datum shmId)
188189
* PGSharedMemoryIsInUse
189190
*
190191
* Is a previously-existing shmem segment still existing and in use?
192+
*
193+
* The point of this exercise is to detect the case where a prior postmaster
194+
* crashed, but it left child backends that are still running. Therefore
195+
* we only care about shmem segments that are associated with the intended
196+
* DataDir. This is an important consideration since accidental matches of
197+
* shmem segment IDs are reasonably common.
191198
*/
192199
bool
193200
PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
194201
{
195202
IpcMemoryId shmId = (IpcMemoryId) id2;
196203
struct shmid_ds shmStat;
204+
#ifndef WIN32
205+
struct stat statbuf;
206+
PGShmemHeader *hdr;
207+
#endif
197208

198209
/*
199210
* We detect whether a shared memory segment is in use by seeing
200211
* whether it (a) exists and (b) has any processes are attached to it.
201-
*
202-
* If we are unable to perform the stat operation for a reason other than
203-
* nonexistence of the segment (most likely, because it doesn't belong
204-
* to our userid), assume it is in use.
205212
*/
206213
if (shmctl(shmId, IPC_STAT, &shmStat) < 0)
207214
{
@@ -212,13 +219,58 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
212219
*/
213220
if (errno == EINVAL)
214221
return false;
215-
/* Else assume segment is in use */
222+
/*
223+
* EACCES implies that the segment belongs to some other userid,
224+
* which means it is not a Postgres shmem segment (or at least,
225+
* not one that is relevant to our data directory).
226+
*/
227+
if (errno == EACCES)
228+
return false;
229+
/*
230+
* Otherwise, we had better assume that the segment is in use.
231+
* The only likely case is EIDRM, which implies that the segment
232+
* has been IPC_RMID'd but there are still processes attached to it.
233+
*/
216234
return true;
217235
}
218-
/* If it has attached processes, it's in use */
219-
if (shmStat.shm_nattch != 0)
220-
return true;
221-
return false;
236+
237+
/* If it has no attached processes, it's not in use */
238+
if (shmStat.shm_nattch == 0)
239+
return false;
240+
241+
/*
242+
* Try to attach to the segment and see if it matches our data directory.
243+
* This avoids shmid-conflict problems on machines that are running
244+
* several postmasters under the same userid. On Windows, which doesn't
245+
* have useful inode numbers, we can't do this so we punt and assume there
246+
* is a conflict.
247+
*/
248+
#ifndef WIN32
249+
if (stat(DataDir, &statbuf) < 0)
250+
return true; /* if can't stat, be conservative */
251+
252+
hdr = (PGShmemHeader *) shmat(shmId, NULL, PG_SHMAT_FLAGS);
253+
254+
if (hdr == (PGShmemHeader *) -1)
255+
return true; /* if can't attach, be conservative */
256+
257+
if (hdr->magic != PGShmemMagic ||
258+
hdr->device != statbuf.st_dev ||
259+
hdr->inode != statbuf.st_ino)
260+
{
261+
/*
262+
* It's either not a Postgres segment, or not one for my data
263+
* directory. In either case it poses no threat.
264+
*/
265+
shmdt((void *) hdr);
266+
return false;
267+
}
268+
269+
/* Trouble --- looks a lot like there's still live backends */
270+
shmdt((void *) hdr);
271+
#endif
272+
273+
return true;
222274
}
223275

224276

@@ -247,6 +299,9 @@ PGSharedMemoryCreate(uint32 size, bool makePrivate, int port)
247299
void *memAddress;
248300
PGShmemHeader *hdr;
249301
IpcMemoryId shmid;
302+
#ifndef WIN32
303+
struct stat statbuf;
304+
#endif
250305

251306
#ifdef EXEC_BACKEND
252307
/* If Exec case, just attach and return the pointer */
@@ -345,6 +400,17 @@ PGSharedMemoryCreate(uint32 size, bool makePrivate, int port)
345400
hdr->creatorPID = getpid();
346401
hdr->magic = PGShmemMagic;
347402

403+
#ifndef WIN32
404+
/* Fill in the data directory ID info, too */
405+
if (stat(DataDir, &statbuf) < 0)
406+
ereport(FATAL,
407+
(errcode_for_file_access(),
408+
errmsg("could not stat data directory \"%s\": %m",
409+
DataDir)));
410+
hdr->device = statbuf.st_dev;
411+
hdr->inode = statbuf.st_ino;
412+
#endif
413+
348414
/*
349415
* Initialize space allocation status for segment.
350416
*/
@@ -397,15 +463,7 @@ PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
397463
if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
398464
return NULL;
399465

400-
hdr = (PGShmemHeader *) shmat(*shmid,
401-
UsedShmemSegAddr,
402-
#ifdef SHM_SHARE_MMU
403-
/* use intimate shared memory on Solaris */
404-
SHM_SHARE_MMU
405-
#else
406-
0
407-
#endif
408-
);
466+
hdr = (PGShmemHeader *) shmat(*shmid, UsedShmemSegAddr, PG_SHMAT_FLAGS);
409467

410468
if (hdr == (PGShmemHeader *) -1)
411469
return NULL; /* failed: must be some other app's */

src/include/storage/pg_shmem.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
1818
* Portions Copyright (c) 1994, Regents of the University of California
1919
*
20-
* $PostgreSQL: pgsql/src/include/storage/pg_shmem.h,v 1.11 2004/08/29 04:13:10 momjian Exp $
20+
* $PostgreSQL: pgsql/src/include/storage/pg_shmem.h,v 1.12 2004/11/09 21:30:18 tgl Exp $
2121
*
2222
*-------------------------------------------------------------------------
2323
*/
@@ -27,10 +27,14 @@
2727
typedef struct PGShmemHeader /* standard header for all Postgres shmem */
2828
{
2929
int32 magic; /* magic # to identify Postgres segments */
30-
#define PGShmemMagic 679834892
30+
#define PGShmemMagic 679834893
3131
pid_t creatorPID; /* PID of creating process */
3232
uint32 totalsize; /* total size of segment */
3333
uint32 freeoffset; /* offset to first free space */
34+
#ifndef WIN32 /* Windows doesn't have useful inode#s */
35+
dev_t device; /* device data directory is on */
36+
ino_t inode; /* inode number of data directory */
37+
#endif
3438
} PGShmemHeader;
3539

3640

0 commit comments

Comments
 (0)