xref: /csrg-svn/sys/kern/sysv_shm.c (revision 42922)
141490Smckusick /*
241490Smckusick  * Copyright (c) 1988 University of Utah.
341490Smckusick  * Copyright (c) 1990 The Regents of the University of California.
441490Smckusick  * All rights reserved.
541490Smckusick  *
641490Smckusick  * This code is derived from software contributed to Berkeley by
741490Smckusick  * the Systems Programming Group of the University of Utah Computer
841490Smckusick  * Science Department. Originally from University of Wisconsin.
941490Smckusick  *
1041490Smckusick  * %sccs.include.redist.c%
1141490Smckusick  *
1241490Smckusick  * from: Utah $Hdr: uipc_shm.c 1.9 89/08/14$
1341490Smckusick  *
14*42922Smckusick  *	@(#)sysv_shm.c	7.4 (Berkeley) 06/05/90
1541490Smckusick  */
1641490Smckusick 
1741490Smckusick /*
1841490Smckusick  * System V shared memory routines.
1941490Smckusick  */
2041490Smckusick 
2141490Smckusick #ifdef SYSVSHM
2241490Smckusick 
2341490Smckusick #include "machine/pte.h"
2441490Smckusick 
2541490Smckusick #include "param.h"
2641490Smckusick #include "systm.h"
2741490Smckusick #include "user.h"
2841490Smckusick #include "kernel.h"
2941490Smckusick #include "proc.h"
3041490Smckusick #include "vm.h"
3141490Smckusick #include "shm.h"
3241490Smckusick #include "mapmem.h"
3341490Smckusick #include "malloc.h"
3441490Smckusick 
3541490Smckusick #ifdef HPUXCOMPAT
3641490Smckusick #include "../hpux/hpux.h"
3741490Smckusick #endif
3841490Smckusick 
3941490Smckusick int	shmat(), shmctl(), shmdt(), shmget();
4041490Smckusick int	(*shmcalls[])() = { shmat, shmctl, shmdt, shmget };
4141490Smckusick int	shmtot = 0;
4241490Smckusick 
4341490Smckusick int	shmfork(), shmexit();
4441490Smckusick struct	mapmemops shmops = { shmfork, (int (*)())0, shmexit, shmexit };
4541490Smckusick 
4641490Smckusick shminit()
4741490Smckusick {
4841490Smckusick 	register int i;
4941490Smckusick 
5041490Smckusick 	if (shminfo.shmmni > SHMMMNI)
5141490Smckusick 		shminfo.shmmni = SHMMMNI;
5241490Smckusick 	for (i = 0; i < shminfo.shmmni; i++) {
5341490Smckusick 		shmsegs[i].shm_perm.mode = 0;
5441490Smckusick 		shmsegs[i].shm_perm.seq = 0;
5541490Smckusick 	}
5641490Smckusick }
5741490Smckusick 
5841490Smckusick /* entry point for all SHM calls */
5941490Smckusick shmsys()
6041490Smckusick {
6141490Smckusick 	struct a {
6241490Smckusick 		int which;
6341490Smckusick 	} *uap = (struct a *)u.u_ap;
6441490Smckusick 
6541490Smckusick 	if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) {
6641490Smckusick 		u.u_error = EINVAL;
6741490Smckusick 		return;
6841490Smckusick 	}
6941490Smckusick 	(*shmcalls[uap->which])(u.u_ap+1);
7041490Smckusick }
7141490Smckusick 
7241490Smckusick /* get a shared memory segment */
7341490Smckusick shmget(ap)
7441490Smckusick 	int *ap;
7541490Smckusick {
7641490Smckusick 	register struct a {
7741490Smckusick 		key_t key;
7841490Smckusick 		int size;
7941490Smckusick 		int shmflg;
8041490Smckusick 	} *uap = (struct a *)ap;
81*42922Smckusick 	struct proc *p = u.u_procp;
8241490Smckusick 	register struct shmid_ds *shp;
8341490Smckusick 	register int i;
8441490Smckusick 	int rval = 0, size;
8541490Smckusick 	caddr_t kva;
8641490Smckusick 
8741490Smckusick 	/* look up the specified shm_id */
8841490Smckusick 	if (uap->key != IPC_PRIVATE) {
8941490Smckusick 		for (i = 0; i < shminfo.shmmni; i++)
9041490Smckusick 			if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) &&
9141490Smckusick 			    shmsegs[i].shm_perm.key == uap->key) {
9241490Smckusick 				rval = i;
9341490Smckusick 				break;
9441490Smckusick 			}
9541490Smckusick 	} else
9641490Smckusick 		i = shminfo.shmmni;
9741490Smckusick 
9841490Smckusick 	/* create a new shared segment if necessary */
9941490Smckusick 	if (i == shminfo.shmmni) {
10041490Smckusick 		if ((uap->shmflg & IPC_CREAT) == 0) {
10141490Smckusick 			u.u_error = ENOENT;
10241490Smckusick 			return;
10341490Smckusick 		}
10441490Smckusick 		if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) {
10541490Smckusick 			u.u_error = EINVAL;
10641490Smckusick 			return;
10741490Smckusick 		}
10841490Smckusick 		for (i = 0; i < shminfo.shmmni; i++)
10941490Smckusick 			if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) == 0) {
11041490Smckusick 				rval = i;
11141490Smckusick 				break;
11241490Smckusick 			}
11341490Smckusick 		if (i == shminfo.shmmni) {
11441490Smckusick 			u.u_error = ENOSPC;
11541490Smckusick 			return;
11641490Smckusick 		}
11741490Smckusick 		size = clrnd(btoc(uap->size));
11841490Smckusick 		if (shmtot + size > shminfo.shmall) {
11941490Smckusick 			u.u_error = ENOMEM;
12041490Smckusick 			return;
12141490Smckusick 		}
12241490Smckusick 		shp = &shmsegs[rval];
12341490Smckusick 		/*
12441490Smckusick 		 * We need to do a couple of things to ensure consistency
12541490Smckusick 		 * in case we sleep in malloc().  We mark segment as
12641490Smckusick 		 * allocated so that other shmgets() will not allocate it.
12741490Smckusick 		 * We mark it as "destroyed" to insure that shmvalid() is
12841490Smckusick 		 * false making most operations fail (XXX).  We set the key,
12941490Smckusick 		 * so that other shmget()s will fail.
13041490Smckusick 		 */
13141490Smckusick 		shp->shm_perm.mode = SHM_ALLOC | SHM_DEST;
13241490Smckusick 		shp->shm_perm.key = uap->key;
13341490Smckusick 		kva = (caddr_t) malloc((u_long)ctob(size), M_SHM, M_WAITOK);
13441490Smckusick 		if (kva == NULL) {
13541490Smckusick 			shp->shm_perm.mode = 0;
13641490Smckusick 			u.u_error = ENOMEM;
13741490Smckusick 			return;
13841490Smckusick 		}
13941490Smckusick 		if (!claligned(kva))
14041490Smckusick 			panic("shmget: non-aligned memory");
14141490Smckusick 		bzero(kva, (u_int)ctob(size));
14241490Smckusick 		shmtot += size;
14341490Smckusick 		shp->shm_perm.cuid = shp->shm_perm.uid = u.u_uid;
14441490Smckusick 		shp->shm_perm.cgid = shp->shm_perm.gid = u.u_gid;
14541490Smckusick 		shp->shm_perm.mode = SHM_ALLOC | (uap->shmflg&0777);
14641490Smckusick 		shp->shm_handle = (void *) kvtopte(kva);
14741490Smckusick 		shp->shm_segsz = uap->size;
148*42922Smckusick 		shp->shm_cpid = p->p_pid;
14941490Smckusick 		shp->shm_lpid = shp->shm_nattch = 0;
15041490Smckusick 		shp->shm_atime = shp->shm_dtime = 0;
15141490Smckusick 		shp->shm_ctime = time.tv_sec;
15241490Smckusick 	} else {
15341490Smckusick 		shp = &shmsegs[rval];
15441490Smckusick 		/* XXX: probably not the right thing to do */
15541490Smckusick 		if (shp->shm_perm.mode & SHM_DEST) {
15641490Smckusick 			u.u_error = EBUSY;
15741490Smckusick 			return;
15841490Smckusick 		}
15941490Smckusick 		if (!ipcaccess(&shp->shm_perm, uap->shmflg&0777))
16041490Smckusick 			return;
16141490Smckusick 		if (uap->size && uap->size > shp->shm_segsz) {
16241490Smckusick 			u.u_error = EINVAL;
16341490Smckusick 			return;
16441490Smckusick 		}
16541490Smckusick 		if ((uap->shmflg&IPC_CREAT) && (uap->shmflg&IPC_EXCL)) {
16641490Smckusick 			u.u_error = EEXIST;
16741490Smckusick 			return;
16841490Smckusick 		}
16941490Smckusick 	}
17041490Smckusick 	u.u_r.r_val1 = shp->shm_perm.seq * SHMMMNI + rval;
17141490Smckusick }
17241490Smckusick 
17341490Smckusick /* shared memory control */
17441490Smckusick shmctl(ap)
17541490Smckusick 	int *ap;
17641490Smckusick {
17741490Smckusick 	register struct a {
17841490Smckusick 		int shmid;
17941490Smckusick 		int cmd;
18041490Smckusick 		caddr_t buf;
18141490Smckusick 	} *uap = (struct a *)ap;
182*42922Smckusick 	struct proc *p = u.u_procp;
18341490Smckusick 	register struct shmid_ds *shp;
18441490Smckusick 	struct shmid_ds sbuf;
18541490Smckusick 
18641490Smckusick 	if (!shmvalid(uap->shmid))
18741490Smckusick 		return;
18841490Smckusick 	shp = &shmsegs[uap->shmid % SHMMMNI];
18941490Smckusick 	switch (uap->cmd) {
19041490Smckusick 	case IPC_STAT:
19141490Smckusick 		if (ipcaccess(&shp->shm_perm, IPC_R))
19241490Smckusick 			u.u_error =
19341490Smckusick 				copyout((caddr_t)shp, uap->buf, sizeof(*shp));
19441490Smckusick 		break;
19541490Smckusick 
19641490Smckusick 	case IPC_SET:
19741490Smckusick 		if (u.u_uid && u.u_uid != shp->shm_perm.uid &&
19841490Smckusick 		    u.u_uid != shp->shm_perm.cuid) {
19941490Smckusick 			u.u_error = EPERM;
20041490Smckusick 			break;
20141490Smckusick 		}
20241490Smckusick 		u.u_error = copyin(uap->buf, (caddr_t)&sbuf, sizeof sbuf);
20341490Smckusick 		if (!u.u_error) {
20441490Smckusick 			shp->shm_perm.uid = sbuf.shm_perm.uid;
20541490Smckusick 			shp->shm_perm.gid = sbuf.shm_perm.gid;
20641490Smckusick 			shp->shm_perm.mode = (shp->shm_perm.mode & ~0777)
20741490Smckusick 				| (sbuf.shm_perm.mode & 0777);
20841490Smckusick 			shp->shm_ctime = time.tv_sec;
20941490Smckusick 		}
21041490Smckusick 		break;
21141490Smckusick 
21241490Smckusick 	case IPC_RMID:
21341490Smckusick 		if (u.u_uid && u.u_uid != shp->shm_perm.uid &&
21441490Smckusick 		    u.u_uid != shp->shm_perm.cuid) {
21541490Smckusick 			u.u_error = EPERM;
21641490Smckusick 			break;
21741490Smckusick 		}
21841490Smckusick 		/* set ctime? */
21941490Smckusick 		shp->shm_perm.key = IPC_PRIVATE;
22041490Smckusick 		shp->shm_perm.mode |= SHM_DEST;
22141490Smckusick 		if (shp->shm_nattch <= 0)
22241490Smckusick 			shmfree(shp);
22341490Smckusick 		break;
22441490Smckusick 
22541490Smckusick #ifdef HPUXCOMPAT
22641490Smckusick 	case SHM_LOCK:
22741490Smckusick 	case SHM_UNLOCK:
22841490Smckusick 		/* don't really do anything, but make them think we did */
229*42922Smckusick 		if ((p->p_flag & SHPUX) == 0)
23041490Smckusick 			u.u_error = EINVAL;
23141490Smckusick 		else if (u.u_uid && u.u_uid != shp->shm_perm.uid &&
23241490Smckusick 			 u.u_uid != shp->shm_perm.cuid)
23341490Smckusick 			u.u_error = EPERM;
23441490Smckusick 		break;
23541490Smckusick #endif
23641490Smckusick 
23741490Smckusick 	default:
23841490Smckusick 		u.u_error = EINVAL;
23941490Smckusick 		break;
24041490Smckusick 	}
24141490Smckusick }
24241490Smckusick 
24341490Smckusick shmat(ap)
24441490Smckusick 	int *ap;
24541490Smckusick {
24641490Smckusick 	struct a {
24741490Smckusick 		int	shmid;
24841490Smckusick 		caddr_t	shmaddr;
24941490Smckusick 		int	shmflg;
25041490Smckusick 	} *uap = (struct a *)ap;
251*42922Smckusick 	struct proc *p = u.u_procp;
25241490Smckusick 	register struct shmid_ds *shp;
25341490Smckusick 	register int size;
25441490Smckusick 	struct mapmem *mp;
25541490Smckusick 	caddr_t uva;
256*42922Smckusick 	int error, prot, shmmapin();
25741490Smckusick 
25841490Smckusick 	if (!shmvalid(uap->shmid))
25941490Smckusick 		return;
26041490Smckusick 	shp = &shmsegs[uap->shmid % SHMMMNI];
26141490Smckusick 	if (shp->shm_handle == NULL)
26242349Smckusick 		panic("shmat NULL handle");
26341490Smckusick 	if (!ipcaccess(&shp->shm_perm,
26441490Smckusick 		      (uap->shmflg&SHM_RDONLY) ? IPC_R : IPC_R|IPC_W))
26541490Smckusick 		return;
26641490Smckusick 	uva = uap->shmaddr;
26741490Smckusick 	if (uva && ((int)uva & (SHMLBA-1))) {
26841490Smckusick 		if (uap->shmflg & SHM_RND)
26941490Smckusick 			uva = (caddr_t) ((int)uva & ~(SHMLBA-1));
27041490Smckusick 		else {
27141490Smckusick 			u.u_error = EINVAL;
27241490Smckusick 			return;
27341490Smckusick 		}
27441490Smckusick 	}
27541490Smckusick 	/*
27641490Smckusick 	 * Make sure user doesn't use more than their fair share
27741490Smckusick 	 */
27841490Smckusick 	size = 0;
27941490Smckusick 	for (mp = u.u_mmap; mp; mp = mp->mm_next)
28041490Smckusick 		if (mp->mm_ops == &shmops)
28141490Smckusick 			size++;
28241490Smckusick 	if (size >= shminfo.shmseg) {
28341490Smckusick 		u.u_error = EMFILE;
28441490Smckusick 		return;
28541490Smckusick 	}
28641490Smckusick 	/*
28741490Smckusick 	 * Allocate a mapped memory region descriptor and
28841490Smckusick 	 * attempt to expand the user page table to allow for region
28941490Smckusick 	 */
29041490Smckusick 	prot = (uap->shmflg & SHM_RDONLY) ? MM_RO : MM_RW;
29141490Smckusick #if defined(hp300)
29241490Smckusick 	prot |= MM_CI;
29341490Smckusick #endif
29441490Smckusick 	size = ctob(clrnd(btoc(shp->shm_segsz)));
295*42922Smckusick 	error = mmalloc(p, uap->shmid, &uva, (segsz_t)size, prot, &shmops, &mp);
296*42922Smckusick 	if (error) {
297*42922Smckusick 		u.u_error = error;
29841490Smckusick 		return;
299*42922Smckusick 	}
300*42922Smckusick 	if (u.u_error = mmmapin(p, mp, shmmapin)) {
301*42922Smckusick 		if (error = mmfree(p, mp))
302*42922Smckusick 			u.u_error = error;
30341490Smckusick 		return;
30441490Smckusick 	}
30541490Smckusick 	/*
30641490Smckusick 	 * Fill in the remaining fields
30741490Smckusick 	 */
308*42922Smckusick 	shp->shm_lpid = p->p_pid;
30941490Smckusick 	shp->shm_atime = time.tv_sec;
31041490Smckusick 	shp->shm_nattch++;
31141490Smckusick 	u.u_r.r_val1 = (int) uva;
31241490Smckusick }
31341490Smckusick 
31441490Smckusick shmdt(ap)
31541490Smckusick 	int *ap;
31641490Smckusick {
31741490Smckusick 	register struct a {
31841490Smckusick 		caddr_t	shmaddr;
31941490Smckusick 	} *uap = (struct a *)ap;
320*42922Smckusick 	struct proc *p = u.u_procp;
32141490Smckusick 	register struct mapmem *mp;
32241490Smckusick 
32341490Smckusick 	for (mp = u.u_mmap; mp; mp = mp->mm_next)
32441490Smckusick 		if (mp->mm_ops == &shmops && mp->mm_uva == uap->shmaddr)
32541490Smckusick 			break;
32641490Smckusick 	if (mp == MMNIL) {
32741490Smckusick 		u.u_error = EINVAL;
32841490Smckusick 		return;
32941490Smckusick 	}
330*42922Smckusick 	shmsegs[mp->mm_id % SHMMMNI].shm_lpid = p->p_pid;
331*42922Smckusick 	u.u_error = shmufree(p, mp);
33241490Smckusick }
33341490Smckusick 
33441490Smckusick shmmapin(mp, off)
33541490Smckusick 	struct mapmem *mp;
33641490Smckusick {
33741490Smckusick 	register struct shmid_ds *shp;
33841490Smckusick 
33941490Smckusick 	shp = &shmsegs[mp->mm_id % SHMMMNI];
34041490Smckusick 	if (off >= ctob(clrnd(btoc(shp->shm_segsz))))
34141490Smckusick 		return(-1);
34241490Smckusick 	return(((struct pte *)shp->shm_handle)[btop(off)].pg_pfnum);
34341490Smckusick }
34441490Smckusick 
34541490Smckusick /*
34641490Smckusick  * Increment attach count on fork
34741490Smckusick  */
34841490Smckusick shmfork(mp, ischild)
34941490Smckusick 	register struct mapmem *mp;
35041490Smckusick {
35141490Smckusick 	if (!ischild)
35241490Smckusick 		shmsegs[mp->mm_id % SHMMMNI].shm_nattch++;
35341490Smckusick }
35441490Smckusick 
35541490Smckusick /*
35641490Smckusick  * Detach from shared memory segment on exit (or exec)
35741490Smckusick  */
35841490Smckusick shmexit(mp)
35941490Smckusick 	register struct mapmem *mp;
36041490Smckusick {
361*42922Smckusick 	struct proc *p = u.u_procp;		/* XXX */
362*42922Smckusick 
363*42922Smckusick 	u.u_error = shmufree(p, mp);
36441490Smckusick }
36541490Smckusick 
36641490Smckusick shmvalid(id)
36741490Smckusick 	register int id;
36841490Smckusick {
36941490Smckusick 	register struct shmid_ds *shp;
37041490Smckusick 
37141490Smckusick 	if (id < 0 || (id % SHMMMNI) >= shminfo.shmmni)
37241490Smckusick 		return(0);
37341490Smckusick 	shp = &shmsegs[id % SHMMMNI];
37441490Smckusick 	if (shp->shm_perm.seq == (id / SHMMMNI) &&
37541490Smckusick 	    (shp->shm_perm.mode & (SHM_ALLOC|SHM_DEST)) == SHM_ALLOC)
37641490Smckusick 		return(1);
37741490Smckusick 	u.u_error = EINVAL;
37841490Smckusick 	return(0);
37941490Smckusick }
38041490Smckusick 
38141490Smckusick /*
38241490Smckusick  * Free user resources associated with a shared memory segment
38341490Smckusick  */
384*42922Smckusick shmufree(p, mp)
385*42922Smckusick 	struct proc *p;
38641490Smckusick 	struct mapmem *mp;
38741490Smckusick {
38841490Smckusick 	register struct shmid_ds *shp;
389*42922Smckusick 	int error;
39041490Smckusick 
39141490Smckusick 	shp = &shmsegs[mp->mm_id % SHMMMNI];
392*42922Smckusick 	mmmapout(p, mp);
393*42922Smckusick 	error = mmfree(p, mp);
39441490Smckusick 	shp->shm_dtime = time.tv_sec;
39541490Smckusick 	if (--shp->shm_nattch <= 0 && (shp->shm_perm.mode & SHM_DEST))
39641490Smckusick 		shmfree(shp);
397*42922Smckusick 	return (error);
39841490Smckusick }
39941490Smckusick 
40041490Smckusick /*
40141490Smckusick  * Deallocate resources associated with a shared memory segment
40241490Smckusick  */
40341490Smckusick shmfree(shp)
40441490Smckusick 	register struct shmid_ds *shp;
40541490Smckusick {
40641490Smckusick 	caddr_t kva;
40741490Smckusick 
40841490Smckusick 	if (shp->shm_handle == NULL)
40941490Smckusick 		panic("shmfree");
41041490Smckusick 	kva = (caddr_t) ptetokv(shp->shm_handle);
41141490Smckusick 	free(kva, M_SHM);
41241490Smckusick 	shp->shm_handle = NULL;
41341490Smckusick 	shmtot -= clrnd(btoc(shp->shm_segsz));
41441490Smckusick 	shp->shm_perm.mode = 0;
41541490Smckusick 	/*
41641490Smckusick 	 * Increment the sequence number to ensure that outstanding
41741490Smckusick 	 * shmids for this segment will be invalid in the event that
41841490Smckusick 	 * the segment is reallocated.  Note that shmids must be
41941490Smckusick 	 * positive as decreed by SVID.
42041490Smckusick 	 */
42141490Smckusick 	shp->shm_perm.seq++;
42241490Smckusick 	if ((int)(shp->shm_perm.seq * SHMMMNI) < 0)
42341490Smckusick 		shp->shm_perm.seq = 0;
42441490Smckusick }
42541490Smckusick 
42641490Smckusick /*
42741490Smckusick  * XXX This routine would be common to all sysV style IPC
42841490Smckusick  *     (if the others were implemented).
42941490Smckusick  */
43041490Smckusick ipcaccess(ipc, mode)
43141490Smckusick 	register struct ipc_perm *ipc;
43241490Smckusick {
43341490Smckusick 	register int m;
43441490Smckusick 
43541490Smckusick 	if (u.u_uid == 0)
43641490Smckusick 		return(0);
43741490Smckusick 	/*
43841490Smckusick 	 * Access check is based on only one of owner, group, public.
43941490Smckusick 	 * If not owner, then check group.
44041490Smckusick 	 * If not a member of the group, then check public access.
44141490Smckusick 	 */
44241490Smckusick 	mode &= 0700;
44341490Smckusick 	m = ipc->mode;
44441490Smckusick 	if (u.u_uid != ipc->uid && u.u_uid != ipc->cuid) {
44541490Smckusick 		m <<= 3;
44641490Smckusick 		if (!groupmember(ipc->gid, u.u_cred) &&
44741490Smckusick 		    !groupmember(ipc->cgid, u.u_cred))
44841490Smckusick 			m <<= 3;
44941490Smckusick 	}
45041490Smckusick 	if ((mode&m) == mode)
45141490Smckusick 		return (1);
45241490Smckusick 	u.u_error = EACCES;
45341490Smckusick 	return (0);
45441490Smckusick }
45541490Smckusick 
45641490Smckusick #endif /* SYSVSHM */
457