Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 2 Jun 1999 18:02:53 -0700 (PDT)
From:      brooks@one-eyed-alien.net
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/11999: Non-standard error checking in shmget
Message-ID:  <199906030102.SAA00504@frigga.aero.org>

next in thread | raw e-mail | index | archive | help

>Number:         11999
>Category:       kern
>Synopsis:       shmget's error handling is not consistant with other unices.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jun  2 18:10:01 PDT 1999
>Closed-Date:
>Last-Modified:
>Originator:     Brooks Davis
>Release:        FreeBSD 3.2-STABLE i386
>Organization:
>Environment:

# uname -v
FreeBSD 3.2-STABLE #4: Wed Jun  2 17:09:25 PDT 1999     brooks@frigga.aero.org:/usr/src/sys/compile/FRIGGA 

>Description:

In applications which require a shared memory segment to be created and
then attached to by a number of processes, a segment must be created
with a unique key and then the key must be passed to the other processes
so they can attach to the segment.  An easy way to do this is to call
semget with random keys and the IPC_EXCL option set until it suceeds.

key_t gs_sysv_keygen( int num_elements, size_t element_size )
  {
    int shmid;
    key_t key = getpid();
    
    while( ( shmid = shmget( key, num_elements * element_size,
                             SHM_R | SHM_W | IPC_CREAT | IPC_EXCL ) ) == -1 )
      {
        /*
         * If we failed for some reason other then existance of a previous
         * shm segment with this key then just bail out now since all the
         * other errors are non-recoverable at this level.
         */
        if( errno != EEXIST )
            return -1;
        key++;
      }

    return key;
  }

This code works under Solaris 2.5.1 and IRIX 6.4.  However, it doesn't
always work under FreeBSD 3.2-STABLE and a quick look at the repository
indicates that is won't work under current either.  The problem is that
other unices check for existance before other fatal errors, but FreeBSD
doesn't.  It checks both permission and size before checking for
existance and IPC_EXCL.

>How-To-Repeat:

/*
 * On Irix and Solaris this code prints:
 *
 * # ./a.out 5
 * key: 5
 * shmget of 1024 bytes with IPC_CREAT and IPC_EXCL: success
 * shmget of 1024 bytes with IPC_CREAT and IPC_EXCL: error: File exists
 * shmget of 2048 bytes with IPC_CREAT and IPC_EXCL: error: File exists
 *
 * On FreeBSD the second error is "Invalid argument" instead of "File
 * exists".
 *
 */

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(int argc, char **argv)
  {
    key_t shmkey;
    int shmid[3];

    if(argc != 2)
      {
        fprintf(stderr, "Usage: %s <shmkey>\n", argv[0]);
        exit(-1);
      }
    shmkey = atoi(argv[1]);
    printf("key: %d\n", shmkey);

    printf("shmget of 1024 bytes with IPC_CREAT and IPC_EXCL: ");
    errno = 0;
    if((shmid[0] = shmget(shmkey, 1024, IPC_CREAT | IPC_EXCL)) == -1)
        printf("error: %s\n", strerror(errno));
    else
        printf("success\n");

    printf("shmget of 1024 bytes with IPC_CREAT and IPC_EXCL: ");
    errno = 0;
    if((shmid[1] = shmget(shmkey, 1024, IPC_CREAT | IPC_EXCL)) == -1)
        printf("error: %s\n", strerror(errno));
    else
        printf("success\n");

    printf("shmget of 2048 bytes with IPC_CREAT and IPC_EXCL: ");
    errno = 0;
    if((shmid[2] = shmget(shmkey, 2048, IPC_CREAT | IPC_EXCL)) == -1)
        printf("error: %s\n", strerror(errno));
    else
        printf("success\n");

    if(shmid[0] != -1)
        shmctl(shmid[0], IPC_RMID, NULL);
    if(shmid[1] != -1)
        shmctl(shmid[1], IPC_RMID, NULL);
    if(shmid[2] != -1)
        shmctl(shmid[2], IPC_RMID, NULL);
  }

>Fix:
	
The following patch to src/sys/kern/sysv_shm.c fixes the problem.

*** sysv_shm.c.orig	Wed Jun  2 17:00:43 1999
--- sysv_shm.c	Wed Jun  2 17:01:35 1999
***************
*** 444,456 ****
  			return error;
  		return EAGAIN;
  	}
  	error = ipcperm(cred, &shmseg->shm_perm, mode);
  	if (error)
  		return error;
  	if (uap->size && uap->size > shmseg->shm_segsz)
  		return EINVAL;
-        if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
- 		return EEXIST;
  	p->p_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
  	return 0;
  }
--- 444,456 ----
  			return error;
  		return EAGAIN;
  	}
+ 	if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
+ 		return EEXIST;
  	error = ipcperm(cred, &shmseg->shm_perm, mode);
  	if (error)
  		return error;
  	if (uap->size && uap->size > shmseg->shm_segsz)
  		return EINVAL;
  	p->p_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
  	return 0;
  }

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199906030102.SAA00504>