Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1541632

Browse files
committed
Add code to InternalIpcMemoryCreate() to handle the case where shmget()
returns EINVAL for an existing shared memory segment. Although it's not terribly sensible, that behavior does meet the POSIX spec because EINVAL is the appropriate error code when the existing segment is smaller than the requested size, and the spec explicitly disclaims any particular ordering of error checks. Moreover, it does in fact happen on OS X and probably other BSD-derived kernels. (We were able to talk NetBSD into changing their code, but purging that behavior from the wild completely seems unlikely to happen.) We need to distinguish collision with a pre-existing segment from invalid size request in order to behave sensibly, so it's worth some extra code here to get it right. Per report from Gavin Kistner and subsequent investigation. Back-patch to all supported versions, since any of them could get used with a kernel having the debatable behavior.
1 parent 170456c commit 1541632

File tree

1 file changed

+43
-1
lines changed

1 file changed

+43
-1
lines changed

src/backend/port/sysv_shmem.c

+43-1
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.55 2010/01/02 16:57:50 momjian Exp $
13+
* $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.56 2010/05/01 22:46:30 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -92,6 +92,48 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
9292
)
9393
return NULL;
9494

95+
/*
96+
* Some BSD-derived kernels are known to return EINVAL, not EEXIST,
97+
* if there is an existing segment but it's smaller than "size"
98+
* (this is a result of poorly-thought-out ordering of error tests).
99+
* To distinguish between collision and invalid size in such cases,
100+
* we make a second try with size = 0. These kernels do not test
101+
* size against SHMMIN in the preexisting-segment case, so we will
102+
* not get EINVAL a second time if there is such a segment.
103+
*/
104+
if (errno == EINVAL)
105+
{
106+
int save_errno = errno;
107+
108+
shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection);
109+
110+
if (shmid < 0)
111+
{
112+
/* As above, fail quietly if we verify a collision */
113+
if (errno == EEXIST || errno == EACCES
114+
#ifdef EIDRM
115+
|| errno == EIDRM
116+
#endif
117+
)
118+
return NULL;
119+
/* Otherwise, fall through to report the original error */
120+
}
121+
else
122+
{
123+
/*
124+
* On most platforms we cannot get here because SHMMIN is
125+
* greater than zero. However, if we do succeed in creating
126+
* a zero-size segment, free it and then fall through to
127+
* report the original error.
128+
*/
129+
if (shmctl(shmid, IPC_RMID, NULL) < 0)
130+
elog(LOG, "shmctl(%d, %d, 0) failed: %m",
131+
(int) shmid, IPC_RMID);
132+
}
133+
134+
errno = save_errno;
135+
}
136+
95137
/*
96138
* Else complain and abort
97139
*/

0 commit comments

Comments
 (0)