Re: `pg_ctl init` crashes when run concurrently; semget(2) suspected - Mailing list pgsql-hackers

From Tom Lane
Subject Re: `pg_ctl init` crashes when run concurrently; semget(2) suspected
Date
Msg-id 2685565.1754862843@sss.pgh.pa.us
Whole thread Raw
In response to Re: `pg_ctl init` crashes when run concurrently; semget(2) suspected  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: `pg_ctl init` crashes when run concurrently; semget(2) suspected
List pgsql-hackers
I wrote:
> This is from current macOS, but equivalent text appears on Linux and
> in the POSIX spec.  So it's just luck that nobody has reported the
> same problem elsewhere --- unless maybe there is some macOS-specific
> behavior making it more likely that different installs would try the
> same key.

Hmm, no, there is a platform dependency here.  I made the attached
test program to see what happens when there's a key collision,
and on Linux I get

semget(SEMAS_PER_SET) failed: File exists
semget(SEMAS_PER_SET + 1) failed: File exists

but macOS and NetBSD give

semget(SEMAS_PER_SET) failed: File exists
semget(SEMAS_PER_SET + 1) failed: Invalid argument

I didn't try other BSDen; this might be a NetBSD-ism that Apple
inherited, or maybe it's common among the BSDen.  I don't see any
text in POSIX specifying which errno is to be returned in this case,
so we can't really argue that the behavior is wrong.

            regards, tom lane

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>

typedef key_t IpcSemaphoreKey;    /* semaphore key passed to semget(2) */

#ifndef HAVE_UNION_SEMUN
union semun
{
    int            val;
    struct semid_ds *buf;
    unsigned short *array;
};
#endif

#define SEMAS_PER_SET    16

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


int main(int argc, char **argv)
{
    IpcSemaphoreKey semKey = 42;
    int            semId;
    int            semId2;
    union semun semun;

    /*
     * Find a free semaphore key, and create a set with SEMAS_PER_SET semas.
     */
    for(;;)
    {
        semId = semget(semKey, SEMAS_PER_SET,
                       IPC_CREAT | IPC_EXCL | IPCProtection);
        if (semId >= 0)
            break;
        semKey++;
    }

    /*
     * Check behavior if we try to make another set with same # of semas.
     */
    semId2 = semget(semKey, SEMAS_PER_SET,
                    IPC_CREAT | IPC_EXCL | IPCProtection);
    if (semId2 < 0)
        fprintf(stderr, "semget(SEMAS_PER_SET) failed: %s\n",
                strerror(errno));
    else
        fprintf(stderr, "semget(SEMAS_PER_SET) unexpectedly succeeded\n");

    /*
     * Check behavior if we try to make another set with more semas.
     */
    semId2 = semget(semKey, SEMAS_PER_SET + 1,
                    IPC_CREAT | IPC_EXCL | IPCProtection);
    if (semId2 < 0)
        fprintf(stderr, "semget(SEMAS_PER_SET + 1) failed: %s\n",
                strerror(errno));
    else
        fprintf(stderr, "semget(SEMAS_PER_SET + 1) unexpectedly succeeded\n");

    /*
     * Clean up
     */
    semun.val = 0;                /* unused, but keep compiler quiet */

    if (semctl(semId, 0, IPC_RMID, semun) < 0)
        fprintf(stderr, "semctl(%d, 0, IPC_RMID, ...) failed: %s\n",
                semId, strerror(errno));

    return 0;
}

pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: `pg_ctl init` crashes when run concurrently; semget(2) suspected
Next
From: Tom Lane
Date:
Subject: Re: `pg_ctl init` crashes when run concurrently; semget(2) suspected