sysv_shm.c revision 194941
190075Sobrien/*	$NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $	*/
2169689Skan/*-
3132718Skan * Copyright (c) 1994 Adam Glass and Charles Hannum.  All rights reserved.
490075Sobrien *
590075Sobrien * Redistribution and use in source and binary forms, with or without
690075Sobrien * modification, are permitted provided that the following conditions
790075Sobrien * are met:
890075Sobrien * 1. Redistributions of source code must retain the above copyright
990075Sobrien *    notice, this list of conditions and the following disclaimer.
1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1190075Sobrien *    notice, this list of conditions and the following disclaimer in the
1290075Sobrien *    documentation and/or other materials provided with the distribution.
1390075Sobrien * 3. All advertising materials mentioning features or use of this software
1490075Sobrien *    must display the following acknowledgement:
1590075Sobrien *	This product includes software developed by Adam Glass and Charles
1690075Sobrien *	Hannum.
1790075Sobrien * 4. The names of the authors may not be used to endorse or promote products
1890075Sobrien *    derived from this software without specific prior written permission.
19169689Skan *
20169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
2190075Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22132718Skan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2390075Sobrien * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24132718Skan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25132718Skan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2690075Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2790075Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2890075Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29117395Skan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3090075Sobrien */
3190075Sobrien/*-
3290075Sobrien * Copyright (c) 2003-2005 McAfee, Inc.
3390075Sobrien * All rights reserved.
3490075Sobrien *
3590075Sobrien * This software was developed for the FreeBSD Project in part by McAfee
36117395Skan * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
37117395Skan * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
38169689Skan * program.
39169689Skan *
40169689Skan * Redistribution and use in source and binary forms, with or without
41169689Skan * modification, are permitted provided that the following conditions
42169689Skan * are met:
43169689Skan * 1. Redistributions of source code must retain the above copyright
4490075Sobrien *    notice, this list of conditions and the following disclaimer.
4590075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
4690075Sobrien *    notice, this list of conditions and the following disclaimer in the
4790075Sobrien *    documentation and/or other materials provided with the distribution.
4890075Sobrien *
4990075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
5090075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5190075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5290075Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
5390075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54117395Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55117395Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5690075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5790075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5890075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5990075Sobrien * SUCH DAMAGE.
6090075Sobrien */
6190075Sobrien
6290075Sobrien#include <sys/cdefs.h>
6390075Sobrien__FBSDID("$FreeBSD: head/sys/kern/sysv_shm.c 194941 2009-06-25 07:16:10Z rwatson $");
64117395Skan
6590075Sobrien#include "opt_compat.h"
6690075Sobrien#include "opt_sysvipc.h"
67169689Skan
68169689Skan#include <sys/param.h>
69169689Skan#include <sys/systm.h>
7090075Sobrien#include <sys/kernel.h>
7190075Sobrien#include <sys/limits.h>
7290075Sobrien#include <sys/lock.h>
7390075Sobrien#include <sys/sysctl.h>
74169689Skan#include <sys/shm.h>
75169689Skan#include <sys/proc.h>
7690075Sobrien#include <sys/malloc.h>
7790075Sobrien#include <sys/mman.h>
7890075Sobrien#include <sys/module.h>
7990075Sobrien#include <sys/mutex.h>
8090075Sobrien#include <sys/resourcevar.h>
8190075Sobrien#include <sys/stat.h>
8290075Sobrien#include <sys/syscall.h>
83169689Skan#include <sys/syscallsubr.h>
84169689Skan#include <sys/sysent.h>
8590075Sobrien#include <sys/sysproto.h>
8690075Sobrien#include <sys/jail.h>
8790075Sobrien
8890075Sobrien#include <security/mac/mac_framework.h>
8990075Sobrien
9090075Sobrien#include <vm/vm.h>
9190075Sobrien#include <vm/vm_param.h>
9290075Sobrien#include <vm/pmap.h>
9390075Sobrien#include <vm/vm_object.h>
9490075Sobrien#include <vm/vm_map.h>
9590075Sobrien#include <vm/vm_page.h>
9690075Sobrien#include <vm/vm_pager.h>
97117395Skan
9890075Sobrienstatic MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
9990075Sobrien
100117395Skanstatic int shmget_allocate_segment(struct thread *td,
10190075Sobrien    struct shmget_args *uap, int mode);
102132718Skanstatic int shmget_existing(struct thread *td, struct shmget_args *uap,
103132718Skan    int mode, int segnum);
10490075Sobrien
105132718Skan#define	SHMSEG_FREE     	0x0200
106132718Skan#define	SHMSEG_REMOVED  	0x0400
107132718Skan#define	SHMSEG_ALLOCATED	0x0800
108132718Skan#define	SHMSEG_WANTED		0x1000
109132718Skan
110132718Skanstatic int shm_last_free, shm_nused, shmalloced;
111132718Skanvm_size_t shm_committed;
112132718Skanstatic struct shmid_kernel	*shmsegs;
113132718Skan
114132718Skanstruct shmmap_state {
115132718Skan	vm_offset_t va;
116132718Skan	int shmid;
117132718Skan};
118132718Skan
119132718Skan#if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
120169689Skanstruct oshmctl_args;
121169689Skanstatic int oshmctl(struct thread *td, struct oshmctl_args *uap);
12290075Sobrien#endif
12390075Sobrienstatic void shm_deallocate_segment(struct shmid_kernel *);
124132718Skanstatic int shm_find_segment_by_key(key_t);
12590075Sobrienstatic struct shmid_kernel *shm_find_segment_by_shmid(int);
126132718Skanstatic struct shmid_kernel *shm_find_segment_by_shmidx(int);
12790075Sobrienstatic int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *);
128132718Skanstatic void shmrealloc(void);
129132718Skanstatic void shminit(void);
13090075Sobrienstatic int sysvshm_modload(struct module *, int, void *);
13190075Sobrienstatic int shmunload(void);
13290075Sobrienstatic void shmexit_myhook(struct vmspace *vm);
13390075Sobrienstatic void shmfork_myhook(struct proc *p1, struct proc *p2);
134132718Skanstatic int sysctl_shmsegs(SYSCTL_HANDLER_ARGS);
13590075Sobrien
13690075Sobrien/*
13790075Sobrien * Tuneable values.
13890075Sobrien */
13990075Sobrien#ifndef SHMMAXPGS
14090075Sobrien#define	SHMMAXPGS	8192	/* Note: sysv shared memory is swap backed. */
141169689Skan#endif
142132718Skan#ifndef SHMMAX
14390075Sobrien#define	SHMMAX	(SHMMAXPGS*PAGE_SIZE)
14490075Sobrien#endif
14590075Sobrien#ifndef SHMMIN
14690075Sobrien#define	SHMMIN	1
14790075Sobrien#endif
14890075Sobrien#ifndef SHMMNI
14990075Sobrien#define	SHMMNI	192
150169689Skan#endif
151169689Skan#ifndef SHMSEG
15290075Sobrien#define	SHMSEG	128
153169689Skan#endif
154132718Skan#ifndef SHMALL
155132718Skan#define	SHMALL	(SHMMAXPGS)
15690075Sobrien#endif
157169689Skan
15890075Sobrienstruct	shminfo shminfo = {
159117395Skan	SHMMAX,
16090075Sobrien	SHMMIN,
16190075Sobrien	SHMMNI,
162169689Skan	SHMSEG,
16390075Sobrien	SHMALL
16490075Sobrien};
165169689Skan
16690075Sobrienstatic int shm_use_phys;
16790075Sobrienstatic int shm_allow_removed;
16890075Sobrien
16990075SobrienSYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0,
17090075Sobrien    "Maximum shared memory segment size");
171132718SkanSYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0,
17290075Sobrien    "Minimum shared memory segment size");
17390075SobrienSYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0,
17490075Sobrien    "Number of shared memory identifiers");
17590075SobrienSYSCTL_ULONG(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0,
17690075Sobrien    "Number of segments per process");
17790075SobrienSYSCTL_ULONG(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0,
17890075Sobrien    "Maximum number of pages available for shared memory");
17990075SobrienSYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW,
18090075Sobrien    &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core");
18190075SobrienSYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RW,
18290075Sobrien    &shm_allow_removed, 0,
18390075Sobrien    "Enable/Disable attachment to attached segments marked for removal");
18490075SobrienSYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLFLAG_RD,
18590075Sobrien    NULL, 0, sysctl_shmsegs, "",
18690075Sobrien    "Current number of shared memory segments allocated");
18790075Sobrien
18890075Sobrienstatic int
18990075Sobrienshm_find_segment_by_key(key)
19090075Sobrien	key_t key;
19190075Sobrien{
19290075Sobrien	int i;
19390075Sobrien
19490075Sobrien	for (i = 0; i < shmalloced; i++)
19590075Sobrien		if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) &&
19690075Sobrien		    shmsegs[i].u.shm_perm.key == key)
19790075Sobrien			return (i);
19890075Sobrien	return (-1);
19990075Sobrien}
20090075Sobrien
201132718Skanstatic struct shmid_kernel *
202117395Skanshm_find_segment_by_shmid(int shmid)
20390075Sobrien{
204117395Skan	int segnum;
205132718Skan	struct shmid_kernel *shmseg;
20690075Sobrien
20790075Sobrien	segnum = IPCID_TO_IX(shmid);
208117395Skan	if (segnum < 0 || segnum >= shmalloced)
209117395Skan		return (NULL);
210117395Skan	shmseg = &shmsegs[segnum];
21190075Sobrien	if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
21290075Sobrien	    (!shm_allow_removed &&
213117395Skan	     (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0) ||
214132718Skan	    shmseg->u.shm_perm.seq != IPCID_TO_SEQ(shmid))
21590075Sobrien		return (NULL);
216117395Skan	return (shmseg);
21790075Sobrien}
218117395Skan
219117395Skanstatic struct shmid_kernel *
22090075Sobrienshm_find_segment_by_shmidx(int segnum)
221117395Skan{
222117395Skan	struct shmid_kernel *shmseg;
223117395Skan
224117395Skan	if (segnum < 0 || segnum >= shmalloced)
225132718Skan		return (NULL);
226117395Skan	shmseg = &shmsegs[segnum];
227117395Skan	if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
22890075Sobrien	    (!shm_allow_removed &&
22990075Sobrien	     (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0))
230117395Skan		return (NULL);
231117395Skan	return (shmseg);
232117395Skan}
233117395Skan
234117395Skanstatic void
235117395Skanshm_deallocate_segment(shmseg)
236117395Skan	struct shmid_kernel *shmseg;
23790075Sobrien{
238117395Skan	vm_size_t size;
239117395Skan
240117395Skan	GIANT_REQUIRED;
241117395Skan
242117395Skan	vm_object_deallocate(shmseg->object);
243117395Skan	shmseg->object = NULL;
24490075Sobrien	size = round_page(shmseg->u.shm_segsz);
245117395Skan	shm_committed -= btoc(size);
246117395Skan	shm_nused--;
247117395Skan	shmseg->u.shm_perm.mode = SHMSEG_FREE;
248117395Skan#ifdef MAC
249117395Skan	mac_sysvshm_cleanup(shmseg);
250117395Skan#endif
251117395Skan}
25290075Sobrien
253169689Skanstatic int
254169689Skanshm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s)
255169689Skan{
256117395Skan	struct shmid_kernel *shmseg;
257169689Skan	int segnum, result;
258169689Skan	vm_size_t size;
25990075Sobrien
260117395Skan	GIANT_REQUIRED;
261117395Skan
26290075Sobrien	segnum = IPCID_TO_IX(shmmap_s->shmid);
263117395Skan	shmseg = &shmsegs[segnum];
264117395Skan	size = round_page(shmseg->u.shm_segsz);
26590075Sobrien	result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size);
266117395Skan	if (result != KERN_SUCCESS)
26790075Sobrien		return (EINVAL);
26890075Sobrien	shmmap_s->shmid = -1;
269132718Skan	shmseg->u.shm_dtime = time_second;
27090075Sobrien	if ((--shmseg->u.shm_nattch <= 0) &&
27190075Sobrien	    (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) {
272132718Skan		shm_deallocate_segment(shmseg);
27390075Sobrien		shm_last_free = segnum;
27490075Sobrien	}
27590075Sobrien	return (0);
27690075Sobrien}
277117395Skan
27890075Sobrien#ifndef _SYS_SYSPROTO_H_
27990075Sobrienstruct shmdt_args {
28090075Sobrien	const void *shmaddr;
281117395Skan};
28290075Sobrien#endif
28390075Sobrienint
28490075Sobrienshmdt(td, uap)
285117395Skan	struct thread *td;
28690075Sobrien	struct shmdt_args *uap;
28790075Sobrien{
288169689Skan	struct proc *p = td->td_proc;
289169689Skan	struct shmmap_state *shmmap_s;
290169689Skan#ifdef MAC
291169689Skan	struct shmid_kernel *shmsegptr;
292169689Skan#endif
293169689Skan	int i;
294169689Skan	int error = 0;
295169689Skan
296169689Skan	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
29790075Sobrien		return (ENOSYS);
298117395Skan	mtx_lock(&Giant);
29990075Sobrien	shmmap_s = p->p_vmspace->vm_shm;
30090075Sobrien 	if (shmmap_s == NULL) {
30190075Sobrien		error = EINVAL;
30290075Sobrien		goto done2;
30390075Sobrien	}
30490075Sobrien	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) {
30590075Sobrien		if (shmmap_s->shmid != -1 &&
30690075Sobrien		    shmmap_s->va == (vm_offset_t)uap->shmaddr) {
307169689Skan			break;
308169689Skan		}
30990075Sobrien	}
31090075Sobrien	if (i == shminfo.shmseg) {
31190075Sobrien		error = EINVAL;
31290075Sobrien		goto done2;
31390075Sobrien	}
31490075Sobrien#ifdef MAC
31590075Sobrien	shmsegptr = &shmsegs[IPCID_TO_IX(shmmap_s->shmid)];
31690075Sobrien	error = mac_sysvshm_check_shmdt(td->td_ucred, shmsegptr);
31790075Sobrien	if (error != 0)
31890075Sobrien		goto done2;
31990075Sobrien#endif
32090075Sobrien	error = shm_delete_mapping(p->p_vmspace, shmmap_s);
32190075Sobriendone2:
32290075Sobrien	mtx_unlock(&Giant);
32390075Sobrien	return (error);
32490075Sobrien}
325169689Skan
326169689Skan#ifndef _SYS_SYSPROTO_H_
327169689Skanstruct shmat_args {
328169689Skan	int shmid;
32990075Sobrien	const void *shmaddr;
33090075Sobrien	int shmflg;
33190075Sobrien};
33290075Sobrien#endif
33390075Sobrienint
33490075Sobrienkern_shmat(td, shmid, shmaddr, shmflg)
33590075Sobrien	struct thread *td;
33690075Sobrien	int shmid;
33790075Sobrien	const void *shmaddr;
33890075Sobrien	int shmflg;
339169689Skan{
340169689Skan	struct proc *p = td->td_proc;
341169689Skan	int i, flags;
342169689Skan	struct shmid_kernel *shmseg;
343169689Skan	struct shmmap_state *shmmap_s = NULL;
344169689Skan	vm_offset_t attach_va;
34590075Sobrien	vm_prot_t prot;
34690075Sobrien	vm_size_t size;
34790075Sobrien	int rv;
34890075Sobrien	int error = 0;
349117395Skan
35090075Sobrien	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
35190075Sobrien		return (ENOSYS);
35290075Sobrien	mtx_lock(&Giant);
35390075Sobrien	shmmap_s = p->p_vmspace->vm_shm;
35490075Sobrien	if (shmmap_s == NULL) {
35590075Sobrien		shmmap_s = malloc(shminfo.shmseg * sizeof(struct shmmap_state),
35690075Sobrien		    M_SHM, M_WAITOK);
35790075Sobrien		for (i = 0; i < shminfo.shmseg; i++)
358132718Skan			shmmap_s[i].shmid = -1;
35990075Sobrien		p->p_vmspace->vm_shm = shmmap_s;
36090075Sobrien	}
36190075Sobrien	shmseg = shm_find_segment_by_shmid(shmid);
36290075Sobrien	if (shmseg == NULL) {
36390075Sobrien		error = EINVAL;
36490075Sobrien		goto done2;
36590075Sobrien	}
36690075Sobrien	error = ipcperm(td, &shmseg->u.shm_perm,
36790075Sobrien	    (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
36890075Sobrien	if (error)
36990075Sobrien		goto done2;
37090075Sobrien#ifdef MAC
37190075Sobrien	error = mac_sysvshm_check_shmat(td->td_ucred, shmseg, shmflg);
37290075Sobrien	if (error != 0)
37390075Sobrien		goto done2;
37490075Sobrien#endif
37590075Sobrien	for (i = 0; i < shminfo.shmseg; i++) {
37690075Sobrien		if (shmmap_s->shmid == -1)
37790075Sobrien			break;
37890075Sobrien		shmmap_s++;
37990075Sobrien	}
38090075Sobrien	if (i >= shminfo.shmseg) {
38190075Sobrien		error = EMFILE;
38290075Sobrien		goto done2;
38390075Sobrien	}
38490075Sobrien	size = round_page(shmseg->u.shm_segsz);
38590075Sobrien	prot = VM_PROT_READ;
38690075Sobrien	if ((shmflg & SHM_RDONLY) == 0)
38790075Sobrien		prot |= VM_PROT_WRITE;
38890075Sobrien	flags = MAP_ANON | MAP_SHARED;
38990075Sobrien	if (shmaddr) {
39090075Sobrien		flags |= MAP_FIXED;
39190075Sobrien		if (shmflg & SHM_RND) {
39290075Sobrien			attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1);
39390075Sobrien		} else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) {
39490075Sobrien			attach_va = (vm_offset_t)shmaddr;
39590075Sobrien		} else {
39690075Sobrien			error = EINVAL;
39790075Sobrien			goto done2;
39890075Sobrien		}
39990075Sobrien	} else {
40090075Sobrien		/*
40190075Sobrien		 * This is just a hint to vm_map_find() about where to
40290075Sobrien		 * put it.
40390075Sobrien		 */
40490075Sobrien		PROC_LOCK(p);
40590075Sobrien		attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr +
40690075Sobrien		    lim_max(p, RLIMIT_DATA));
40790075Sobrien		PROC_UNLOCK(p);
40890075Sobrien	}
40990075Sobrien
410169689Skan	vm_object_reference(shmseg->object);
41190075Sobrien	rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object,
41290075Sobrien	    0, &attach_va, size, (flags & MAP_FIXED) ? VMFS_NO_SPACE :
41390075Sobrien	    VMFS_ANY_SPACE, prot, prot, 0);
41490075Sobrien	if (rv != KERN_SUCCESS) {
41590075Sobrien		vm_object_deallocate(shmseg->object);
41690075Sobrien		error = ENOMEM;
41790075Sobrien		goto done2;
41890075Sobrien	}
41990075Sobrien	vm_map_inherit(&p->p_vmspace->vm_map,
42090075Sobrien		attach_va, attach_va + size, VM_INHERIT_SHARE);
42190075Sobrien
42290075Sobrien	shmmap_s->va = attach_va;
42390075Sobrien	shmmap_s->shmid = shmid;
42490075Sobrien	shmseg->u.shm_lpid = p->p_pid;
42590075Sobrien	shmseg->u.shm_atime = time_second;
42690075Sobrien	shmseg->u.shm_nattch++;
42790075Sobrien	td->td_retval[0] = attach_va;
42890075Sobriendone2:
42990075Sobrien	mtx_unlock(&Giant);
43090075Sobrien	return (error);
43190075Sobrien}
43290075Sobrien
43390075Sobrienint
43490075Sobrienshmat(td, uap)
43590075Sobrien	struct thread *td;
43690075Sobrien	struct shmat_args *uap;
43790075Sobrien{
43890075Sobrien	return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg);
43990075Sobrien}
44090075Sobrien
44190075Sobrienint
44290075Sobrienkern_shmctl(td, shmid, cmd, buf, bufsz)
44390075Sobrien	struct thread *td;
44490075Sobrien	int shmid;
44590075Sobrien	int cmd;
446132718Skan	void *buf;
44790075Sobrien	size_t *bufsz;
44890075Sobrien{
44990075Sobrien	int error = 0;
45090075Sobrien	struct shmid_kernel *shmseg;
45190075Sobrien
45290075Sobrien	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
45390075Sobrien		return (ENOSYS);
45490075Sobrien
45590075Sobrien	mtx_lock(&Giant);
45690075Sobrien	switch (cmd) {
45790075Sobrien	/*
45890075Sobrien	 * It is possible that kern_shmctl is being called from the Linux ABI
45990075Sobrien	 * layer, in which case, we will need to implement IPC_INFO.  It should
46090075Sobrien	 * be noted that other shmctl calls will be funneled through here for
46190075Sobrien	 * Linix binaries as well.
46290075Sobrien	 *
46390075Sobrien	 * NB: The Linux ABI layer will convert this data to structure(s) more
464117395Skan	 * consistent with the Linux ABI.
46590075Sobrien	 */
46690075Sobrien	case IPC_INFO:
46790075Sobrien		memcpy(buf, &shminfo, sizeof(shminfo));
46890075Sobrien		if (bufsz)
46990075Sobrien			*bufsz = sizeof(shminfo);
47090075Sobrien		td->td_retval[0] = shmalloced;
471169689Skan		goto done2;
472169689Skan	case SHM_INFO: {
47390075Sobrien		struct shm_info shm_info;
47490075Sobrien		shm_info.used_ids = shm_nused;
47590075Sobrien		shm_info.shm_rss = 0;	/*XXX where to get from ? */
47690075Sobrien		shm_info.shm_tot = 0;	/*XXX where to get from ? */
47790075Sobrien		shm_info.shm_swp = 0;	/*XXX where to get from ? */
47890075Sobrien		shm_info.swap_attempts = 0;	/*XXX where to get from ? */
47990075Sobrien		shm_info.swap_successes = 0;	/*XXX where to get from ? */
48090075Sobrien		memcpy(buf, &shm_info, sizeof(shm_info));
48190075Sobrien		if (bufsz)
48290075Sobrien			*bufsz = sizeof(shm_info);
48390075Sobrien		td->td_retval[0] = shmalloced;
48490075Sobrien		goto done2;
48590075Sobrien	}
48690075Sobrien	}
48790075Sobrien	if (cmd == SHM_STAT)
48890075Sobrien		shmseg = shm_find_segment_by_shmidx(shmid);
48990075Sobrien	else
490169689Skan		shmseg = shm_find_segment_by_shmid(shmid);
491169689Skan	if (shmseg == NULL) {
49290075Sobrien		error = EINVAL;
49390075Sobrien		goto done2;
49490075Sobrien	}
49590075Sobrien#ifdef MAC
49690075Sobrien	error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd);
49790075Sobrien	if (error != 0)
498169689Skan		goto done2;
499169689Skan#endif
50090075Sobrien	switch (cmd) {
50190075Sobrien	case SHM_STAT:
50290075Sobrien	case IPC_STAT:
50390075Sobrien		error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
50490075Sobrien		if (error)
50590075Sobrien			goto done2;
50690075Sobrien		memcpy(buf, &shmseg->u, sizeof(struct shmid_ds));
50790075Sobrien		if (bufsz)
50890075Sobrien			*bufsz = sizeof(struct shmid_ds);
50990075Sobrien		if (cmd == SHM_STAT)
51090075Sobrien			td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->u.shm_perm);
51190075Sobrien		break;
51290075Sobrien	case IPC_SET: {
513169689Skan		struct shmid_ds *shmid;
514169689Skan
51590075Sobrien		shmid = (struct shmid_ds *)buf;
51690075Sobrien		error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
51790075Sobrien		if (error)
51890075Sobrien			goto done2;
51990075Sobrien		shmseg->u.shm_perm.uid = shmid->shm_perm.uid;
52090075Sobrien		shmseg->u.shm_perm.gid = shmid->shm_perm.gid;
521132718Skan		shmseg->u.shm_perm.mode =
52290075Sobrien		    (shmseg->u.shm_perm.mode & ~ACCESSPERMS) |
52390075Sobrien		    (shmid->shm_perm.mode & ACCESSPERMS);
52490075Sobrien		shmseg->u.shm_ctime = time_second;
52590075Sobrien		break;
52690075Sobrien	}
52790075Sobrien	case IPC_RMID:
52890075Sobrien		error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
52990075Sobrien		if (error)
53090075Sobrien			goto done2;
53190075Sobrien		shmseg->u.shm_perm.key = IPC_PRIVATE;
53290075Sobrien		shmseg->u.shm_perm.mode |= SHMSEG_REMOVED;
53390075Sobrien		if (shmseg->u.shm_nattch <= 0) {
53490075Sobrien			shm_deallocate_segment(shmseg);
53590075Sobrien			shm_last_free = IPCID_TO_IX(shmid);
536132718Skan		}
53790075Sobrien		break;
53890075Sobrien#if 0
53990075Sobrien	case SHM_LOCK:
54090075Sobrien	case SHM_UNLOCK:
54190075Sobrien#endif
54290075Sobrien	default:
54390075Sobrien		error = EINVAL;
54490075Sobrien		break;
54590075Sobrien	}
54690075Sobriendone2:
54790075Sobrien	mtx_unlock(&Giant);
548132718Skan	return (error);
54990075Sobrien}
55090075Sobrien
55190075Sobrien#ifndef _SYS_SYSPROTO_H_
55290075Sobrienstruct shmctl_args {
55390075Sobrien	int shmid;
55490075Sobrien	int cmd;
55590075Sobrien	struct shmid_ds *buf;
55690075Sobrien};
55790075Sobrien#endif
55890075Sobrienint
55990075Sobrienshmctl(td, uap)
56090075Sobrien	struct thread *td;
56190075Sobrien	struct shmctl_args *uap;
56290075Sobrien{
56390075Sobrien	int error = 0;
56490075Sobrien	struct shmid_ds buf;
56590075Sobrien	size_t bufsz;
56690075Sobrien
56790075Sobrien	/*
56890075Sobrien	 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
56990075Sobrien	 * Linux binaries.  If we see the call come through the FreeBSD ABI,
57090075Sobrien	 * return an error back to the user since we do not to support this.
57190075Sobrien	 */
57290075Sobrien	if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
57390075Sobrien	    uap->cmd == SHM_STAT)
57490075Sobrien		return (EINVAL);
575132718Skan
57690075Sobrien	/* IPC_SET needs to copyin the buffer before calling kern_shmctl */
57790075Sobrien	if (uap->cmd == IPC_SET) {
57890075Sobrien		if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds))))
57990075Sobrien			goto done;
58090075Sobrien	}
58190075Sobrien
58290075Sobrien	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
58390075Sobrien	if (error)
58490075Sobrien		goto done;
585169689Skan
58690075Sobrien	/* Cases in which we need to copyout */
58790075Sobrien	switch (uap->cmd) {
58890075Sobrien	case IPC_STAT:
58990075Sobrien		error = copyout(&buf, uap->buf, bufsz);
59090075Sobrien		break;
591132718Skan	}
59290075Sobrien
59390075Sobriendone:
59490075Sobrien	if (error) {
59590075Sobrien		/* Invalidate the return value */
59690075Sobrien		td->td_retval[0] = -1;
59790075Sobrien	}
59890075Sobrien	return (error);
59990075Sobrien}
60090075Sobrien
60190075Sobrien
60290075Sobrienstatic int
60390075Sobrienshmget_existing(td, uap, mode, segnum)
60490075Sobrien	struct thread *td;
60590075Sobrien	struct shmget_args *uap;
60690075Sobrien	int mode;
60790075Sobrien	int segnum;
60890075Sobrien{
60990075Sobrien	struct shmid_kernel *shmseg;
61090075Sobrien	int error;
61190075Sobrien
61290075Sobrien	shmseg = &shmsegs[segnum];
61390075Sobrien	if (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) {
61490075Sobrien		/*
61590075Sobrien		 * This segment is in the process of being allocated.  Wait
61690075Sobrien		 * until it's done, and look the key up again (in case the
61790075Sobrien		 * allocation failed or it was freed).
618169689Skan		 */
61990075Sobrien		shmseg->u.shm_perm.mode |= SHMSEG_WANTED;
62090075Sobrien		error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0);
62190075Sobrien		if (error)
62290075Sobrien			return (error);
62390075Sobrien		return (EAGAIN);
62490075Sobrien	}
62590075Sobrien	if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
62690075Sobrien		return (EEXIST);
62790075Sobrien#ifdef MAC
62890075Sobrien	error = mac_sysvshm_check_shmget(td->td_ucred, shmseg, uap->shmflg);
62990075Sobrien	if (error != 0)
630132718Skan		return (error);
63190075Sobrien#endif
63290075Sobrien	if (uap->size != 0 && uap->size > shmseg->u.shm_segsz)
63390075Sobrien		return (EINVAL);
63490075Sobrien	td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
63590075Sobrien	return (0);
63690075Sobrien}
63790075Sobrien
63890075Sobrienstatic int
63990075Sobrienshmget_allocate_segment(td, uap, mode)
64090075Sobrien	struct thread *td;
64190075Sobrien	struct shmget_args *uap;
64290075Sobrien	int mode;
64390075Sobrien{
64490075Sobrien	int i, segnum, shmid;
64590075Sobrien	size_t size;
64690075Sobrien	struct ucred *cred = td->td_ucred;
64790075Sobrien	struct shmid_kernel *shmseg;
64890075Sobrien	vm_object_t shm_object;
64990075Sobrien
65090075Sobrien	GIANT_REQUIRED;
65190075Sobrien
65290075Sobrien	if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
65390075Sobrien		return (EINVAL);
65490075Sobrien	if (shm_nused >= shminfo.shmmni) /* Any shmids left? */
65590075Sobrien		return (ENOSPC);
65690075Sobrien	size = round_page(uap->size);
65790075Sobrien	if (shm_committed + btoc(size) > shminfo.shmall)
65890075Sobrien		return (ENOMEM);
65990075Sobrien	if (shm_last_free < 0) {
66090075Sobrien		shmrealloc();	/* Maybe expand the shmsegs[] array. */
66190075Sobrien		for (i = 0; i < shmalloced; i++)
66290075Sobrien			if (shmsegs[i].u.shm_perm.mode & SHMSEG_FREE)
66390075Sobrien				break;
66490075Sobrien		if (i == shmalloced)
66590075Sobrien			return (ENOSPC);
66690075Sobrien		segnum = i;
66790075Sobrien	} else  {
66890075Sobrien		segnum = shm_last_free;
66990075Sobrien		shm_last_free = -1;
67090075Sobrien	}
671169689Skan	shmseg = &shmsegs[segnum];
67290075Sobrien	/*
67390075Sobrien	 * In case we sleep in malloc(), mark the segment present but deleted
67490075Sobrien	 * so that noone else tries to create the same key.
67590075Sobrien	 */
67690075Sobrien	shmseg->u.shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
67790075Sobrien	shmseg->u.shm_perm.key = uap->key;
67890075Sobrien	shmseg->u.shm_perm.seq = (shmseg->u.shm_perm.seq + 1) & 0x7fff;
679132718Skan	shmid = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
680132718Skan
68190075Sobrien	/*
682169689Skan	 * We make sure that we have allocated a pager before we need
683169689Skan	 * to.
68490075Sobrien	 */
68590075Sobrien	shm_object = vm_pager_allocate(shm_use_phys ? OBJT_PHYS : OBJT_SWAP,
686169689Skan	    0, size, VM_PROT_DEFAULT, 0, cred);
68790075Sobrien	if (shm_object == NULL)
68890075Sobrien		return (ENOMEM);
68990075Sobrien	VM_OBJECT_LOCK(shm_object);
69090075Sobrien	vm_object_clear_flag(shm_object, OBJ_ONEMAPPING);
691169689Skan	vm_object_set_flag(shm_object, OBJ_NOSPLIT);
69290075Sobrien	VM_OBJECT_UNLOCK(shm_object);
693169689Skan
69490075Sobrien	shmseg->object = shm_object;
695169689Skan	shmseg->u.shm_perm.cuid = shmseg->u.shm_perm.uid = cred->cr_uid;
696169689Skan	shmseg->u.shm_perm.cgid = shmseg->u.shm_perm.gid = cred->cr_gid;
69790075Sobrien	shmseg->u.shm_perm.mode = (shmseg->u.shm_perm.mode & SHMSEG_WANTED) |
69890075Sobrien	    (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
699169689Skan	shmseg->u.shm_segsz = uap->size;
70090075Sobrien	shmseg->u.shm_cpid = td->td_proc->p_pid;
70190075Sobrien	shmseg->u.shm_lpid = shmseg->u.shm_nattch = 0;
70290075Sobrien	shmseg->u.shm_atime = shmseg->u.shm_dtime = 0;
70390075Sobrien#ifdef MAC
704169689Skan	mac_sysvshm_create(cred, shmseg);
705169689Skan#endif
70690075Sobrien	shmseg->u.shm_ctime = time_second;
707169689Skan	shm_committed += btoc(size);
70890075Sobrien	shm_nused++;
70990075Sobrien	if (shmseg->u.shm_perm.mode & SHMSEG_WANTED) {
71090075Sobrien		/*
711169689Skan		 * Somebody else wanted this key while we were asleep.  Wake
71290075Sobrien		 * them up now.
71390075Sobrien		 */
71490075Sobrien		shmseg->u.shm_perm.mode &= ~SHMSEG_WANTED;
715132718Skan		wakeup(shmseg);
716132718Skan	}
717132718Skan	td->td_retval[0] = shmid;
71890075Sobrien	return (0);
71990075Sobrien}
720169689Skan
72190075Sobrien#ifndef _SYS_SYSPROTO_H_
72290075Sobrienstruct shmget_args {
72390075Sobrien	key_t key;
72490075Sobrien	size_t size;
72590075Sobrien	int shmflg;
72690075Sobrien};
72790075Sobrien#endif
72890075Sobrienint
72990075Sobrienshmget(td, uap)
73090075Sobrien	struct thread *td;
73190075Sobrien	struct shmget_args *uap;
73290075Sobrien{
73390075Sobrien	int segnum, mode;
73490075Sobrien	int error;
73590075Sobrien
73690075Sobrien	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
73790075Sobrien		return (ENOSYS);
73890075Sobrien	mtx_lock(&Giant);
73990075Sobrien	mode = uap->shmflg & ACCESSPERMS;
740169689Skan	if (uap->key != IPC_PRIVATE) {
74190075Sobrien	again:
74290075Sobrien		segnum = shm_find_segment_by_key(uap->key);
74390075Sobrien		if (segnum >= 0) {
74490075Sobrien			error = shmget_existing(td, uap, mode, segnum);
74590075Sobrien			if (error == EAGAIN)
74690075Sobrien				goto again;
74790075Sobrien			goto done2;
74890075Sobrien		}
74990075Sobrien		if ((uap->shmflg & IPC_CREAT) == 0) {
75090075Sobrien			error = ENOENT;
75190075Sobrien			goto done2;
75290075Sobrien		}
75390075Sobrien	}
75490075Sobrien	error = shmget_allocate_segment(td, uap, mode);
75590075Sobriendone2:
75690075Sobrien	mtx_unlock(&Giant);
75790075Sobrien	return (error);
75890075Sobrien}
75990075Sobrien
76090075Sobrienstatic void
76190075Sobrienshmfork_myhook(p1, p2)
76290075Sobrien	struct proc *p1, *p2;
76390075Sobrien{
764169689Skan	struct shmmap_state *shmmap_s;
76590075Sobrien	size_t size;
766169689Skan	int i;
76790075Sobrien
76890075Sobrien	mtx_lock(&Giant);
76990075Sobrien	size = shminfo.shmseg * sizeof(struct shmmap_state);
77090075Sobrien	shmmap_s = malloc(size, M_SHM, M_WAITOK);
771169689Skan	bcopy(p1->p_vmspace->vm_shm, shmmap_s, size);
77290075Sobrien	p2->p_vmspace->vm_shm = shmmap_s;
77390075Sobrien	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
77490075Sobrien		if (shmmap_s->shmid != -1)
77590075Sobrien			shmsegs[IPCID_TO_IX(shmmap_s->shmid)].u.shm_nattch++;
77690075Sobrien	mtx_unlock(&Giant);
777132718Skan}
77890075Sobrien
77990075Sobrienstatic void
78090075Sobrienshmexit_myhook(struct vmspace *vm)
78190075Sobrien{
78290075Sobrien	struct shmmap_state *base, *shm;
783169689Skan	int i;
784169689Skan
78590075Sobrien	if ((base = vm->vm_shm) != NULL) {
78690075Sobrien		vm->vm_shm = NULL;
78790075Sobrien		mtx_lock(&Giant);
78890075Sobrien		for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) {
78990075Sobrien			if (shm->shmid != -1)
79090075Sobrien				shm_delete_mapping(vm, shm);
79190075Sobrien		}
79290075Sobrien		mtx_unlock(&Giant);
79390075Sobrien		free(base, M_SHM);
79490075Sobrien	}
79590075Sobrien}
79690075Sobrien
79790075Sobrienstatic void
79890075Sobrienshmrealloc(void)
79990075Sobrien{
80090075Sobrien	int i;
80190075Sobrien	struct shmid_kernel *newsegs;
80290075Sobrien
80390075Sobrien	if (shmalloced >= shminfo.shmmni)
80490075Sobrien		return;
80590075Sobrien
80690075Sobrien	newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK);
80790075Sobrien	if (newsegs == NULL)
80890075Sobrien		return;
80990075Sobrien	for (i = 0; i < shmalloced; i++)
81090075Sobrien		bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
81190075Sobrien	for (; i < shminfo.shmmni; i++) {
81290075Sobrien		shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
81390075Sobrien		shmsegs[i].u.shm_perm.seq = 0;
81490075Sobrien#ifdef MAC
81590075Sobrien		mac_sysvshm_init(&shmsegs[i]);
81690075Sobrien#endif
81790075Sobrien	}
81890075Sobrien	free(shmsegs, M_SHM);
81990075Sobrien	shmsegs = newsegs;
82090075Sobrien	shmalloced = shminfo.shmmni;
82190075Sobrien}
82290075Sobrien
82390075Sobrienstatic void
82490075Sobrienshminit()
82590075Sobrien{
82690075Sobrien	int i;
82790075Sobrien
82890075Sobrien	TUNABLE_ULONG_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall);
82990075Sobrien	for (i = PAGE_SIZE; i > 0; i--) {
83090075Sobrien		shminfo.shmmax = shminfo.shmall * i;
83190075Sobrien		if (shminfo.shmmax >= shminfo.shmall)
83290075Sobrien			break;
83390075Sobrien	}
83490075Sobrien	TUNABLE_ULONG_FETCH("kern.ipc.shmmin", &shminfo.shmmin);
83590075Sobrien	TUNABLE_ULONG_FETCH("kern.ipc.shmmni", &shminfo.shmmni);
83690075Sobrien	TUNABLE_ULONG_FETCH("kern.ipc.shmseg", &shminfo.shmseg);
83790075Sobrien	TUNABLE_INT_FETCH("kern.ipc.shm_use_phys", &shm_use_phys);
83890075Sobrien
83990075Sobrien	shmalloced = shminfo.shmmni;
84090075Sobrien	shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK);
84190075Sobrien	if (shmsegs == NULL)
84290075Sobrien		panic("cannot allocate initial memory for sysvshm");
84390075Sobrien	for (i = 0; i < shmalloced; i++) {
84490075Sobrien		shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
845117395Skan		shmsegs[i].u.shm_perm.seq = 0;
846117395Skan#ifdef MAC
847169689Skan		mac_sysvshm_init(&shmsegs[i]);
848169689Skan#endif
849169689Skan	}
850169689Skan	shm_last_free = 0;
851169689Skan	shm_nused = 0;
852169689Skan	shm_committed = 0;
853169689Skan	shmexit_hook = &shmexit_myhook;
854169689Skan	shmfork_hook = &shmfork_myhook;
855169689Skan}
856169689Skan
857169689Skanstatic int
858169689Skanshmunload()
859169689Skan{
860169689Skan#ifdef MAC
861169689Skan	int i;
862169689Skan#endif
863169689Skan
864169689Skan	if (shm_nused > 0)
865169689Skan		return (EBUSY);
866169689Skan
867169689Skan#ifdef MAC
868169689Skan	for (i = 0; i < shmalloced; i++)
869169689Skan		mac_sysvshm_destroy(&shmsegs[i]);
870169689Skan#endif
871169689Skan	free(shmsegs, M_SHM);
872169689Skan	shmexit_hook = NULL;
873169689Skan	shmfork_hook = NULL;
874169689Skan	return (0);
875169689Skan}
876169689Skan
877169689Skanstatic int
878169689Skansysctl_shmsegs(SYSCTL_HANDLER_ARGS)
879169689Skan{
880169689Skan
88190075Sobrien	return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0])));
88290075Sobrien}
88390075Sobrien
88490075Sobrien#if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
88590075Sobrienstruct oshmid_ds {
88690075Sobrien	struct	ipc_perm_old shm_perm;	/* operation perms */
88790075Sobrien	int	shm_segsz;		/* size of segment (bytes) */
888132718Skan	u_short	shm_cpid;		/* pid, creator */
88990075Sobrien	u_short	shm_lpid;		/* pid, last operation */
89090075Sobrien	short	shm_nattch;		/* no. of current attaches */
89190075Sobrien	time_t	shm_atime;		/* last attach time */
89290075Sobrien	time_t	shm_dtime;		/* last detach time */
89390075Sobrien	time_t	shm_ctime;		/* last change time */
89490075Sobrien	void	*shm_handle;		/* internal handle for shm segment */
89590075Sobrien};
89690075Sobrien
89790075Sobrienstruct oshmctl_args {
89890075Sobrien	int shmid;
89990075Sobrien	int cmd;
90090075Sobrien	struct oshmid_ds *ubuf;
901132718Skan};
90290075Sobrien
903169689Skanstatic int
90490075Sobrienoshmctl(td, uap)
90590075Sobrien	struct thread *td;
90690075Sobrien	struct oshmctl_args *uap;
90790075Sobrien{
90890075Sobrien#ifdef COMPAT_43
90990075Sobrien	int error = 0;
91090075Sobrien	struct shmid_kernel *shmseg;
91190075Sobrien	struct oshmid_ds outbuf;
91290075Sobrien
91390075Sobrien	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
914169689Skan		return (ENOSYS);
91590075Sobrien	mtx_lock(&Giant);
916169689Skan	shmseg = shm_find_segment_by_shmid(uap->shmid);
917169689Skan	if (shmseg == NULL) {
918169689Skan		error = EINVAL;
919169689Skan		goto done2;
920169689Skan	}
921169689Skan	switch (uap->cmd) {
92290075Sobrien	case IPC_STAT:
923169689Skan		error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
924169689Skan		if (error)
925169689Skan			goto done2;
926169689Skan#ifdef MAC
92790075Sobrien		error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, uap->cmd);
92890075Sobrien		if (error != 0)
92990075Sobrien			goto done2;
930169689Skan#endif
931169689Skan		ipcperm_new2old(&shmseg->u.shm_perm, &outbuf.shm_perm);
932169689Skan		outbuf.shm_segsz = shmseg->u.shm_segsz;
933169689Skan		outbuf.shm_cpid = shmseg->u.shm_cpid;
93490075Sobrien		outbuf.shm_lpid = shmseg->u.shm_lpid;
935169689Skan		outbuf.shm_nattch = shmseg->u.shm_nattch;
936169689Skan		outbuf.shm_atime = shmseg->u.shm_atime;
937169689Skan		outbuf.shm_dtime = shmseg->u.shm_dtime;
93890075Sobrien		outbuf.shm_ctime = shmseg->u.shm_ctime;
939169689Skan		outbuf.shm_handle = shmseg->object;
940169689Skan		error = copyout(&outbuf, uap->ubuf, sizeof(outbuf));
941169689Skan		if (error)
942169689Skan			goto done2;
943169689Skan		break;
944169689Skan	default:
945169689Skan		error = freebsd7_shmctl(td, (struct shmctl_args *)uap);
946169689Skan		break;
947169689Skan	}
948169689Skandone2:
949169689Skan	mtx_unlock(&Giant);
950169689Skan	return (error);
951169689Skan#else
952169689Skan	return (EINVAL);
953169689Skan#endif
954169689Skan}
955169689Skan
95690075Sobrien/* XXX casting to (sy_call_t *) is bogus, as usual. */
957169689Skanstatic sy_call_t *shmcalls[] = {
958169689Skan	(sy_call_t *)shmat, (sy_call_t *)oshmctl,
959169689Skan	(sy_call_t *)shmdt, (sy_call_t *)shmget,
960169689Skan	(sy_call_t *)freebsd7_shmctl
96190075Sobrien};
962169689Skan
963169689Skanint
964169689Skanshmsys(td, uap)
965169689Skan	struct thread *td;
966169689Skan	/* XXX actually varargs. */
96790075Sobrien	struct shmsys_args /* {
968169689Skan		int	which;
969169689Skan		int	a2;
970169689Skan		int	a3;
971169689Skan		int	a4;
972169689Skan	} */ *uap;
97390075Sobrien{
974169689Skan	int error;
975169689Skan
97690075Sobrien	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
97790075Sobrien		return (ENOSYS);
978169689Skan	if (uap->which < 0 ||
979117395Skan	    uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
980169689Skan		return (EINVAL);
981169689Skan	mtx_lock(&Giant);
982169689Skan	error = (*shmcalls[uap->which])(td, &uap->a2);
98390075Sobrien	mtx_unlock(&Giant);
98490075Sobrien	return (error);
98590075Sobrien}
986169689Skan
987169689SkanSYSCALL_MODULE_HELPER(shmsys);
98890075Sobrien#endif	/* i386 && (COMPAT_FREEBSD4 || COMPAT_43) */
989169689Skan
990169689Skan#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
991169689Skan    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
99290075Sobrien
993169689Skan#define CP(src, dst, fld)	do { (dst).fld = (src).fld; } while (0)
994169689Skan
995169689Skan
996169689Skan#ifndef _SYS_SYSPROTO_H_
997169689Skanstruct freebsd7_shmctl_args {
998169689Skan	int shmid;
999169689Skan	int cmd;
1000169689Skan	struct shmid_ds_old *buf;
1001169689Skan};
1002169689Skan#endif
1003169689Skanint
1004169689Skanfreebsd7_shmctl(td, uap)
1005169689Skan	struct thread *td;
100690075Sobrien	struct freebsd7_shmctl_args *uap;
1007169689Skan{
1008169689Skan	int error = 0;
1009169689Skan	struct shmid_ds_old old;
1010169689Skan	struct shmid_ds buf;
1011169689Skan	size_t bufsz;
1012169689Skan
1013169689Skan	/*
1014169689Skan	 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
1015169689Skan	 * Linux binaries.  If we see the call come through the FreeBSD ABI,
1016169689Skan	 * return an error back to the user since we do not to support this.
1017169689Skan	 */
1018169689Skan	if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
1019169689Skan	    uap->cmd == SHM_STAT)
1020169689Skan		return (EINVAL);
102190075Sobrien
1022169689Skan	/* IPC_SET needs to copyin the buffer before calling kern_shmctl */
1023169689Skan	if (uap->cmd == IPC_SET) {
1024169689Skan		if ((error = copyin(uap->buf, &old, sizeof(old))))
1025169689Skan			goto done;
1026169689Skan		ipcperm_old2new(&old.shm_perm, &buf.shm_perm);
1027169689Skan		CP(old, buf, shm_segsz);
1028169689Skan		CP(old, buf, shm_lpid);
1029169689Skan		CP(old, buf, shm_cpid);
1030169689Skan		CP(old, buf, shm_nattch);
1031169689Skan		CP(old, buf, shm_atime);
1032169689Skan		CP(old, buf, shm_dtime);
1033169689Skan		CP(old, buf, shm_ctime);
1034169689Skan	}
1035169689Skan
1036169689Skan	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
1037169689Skan	if (error)
1038169689Skan		goto done;
1039169689Skan
1040169689Skan	/* Cases in which we need to copyout */
1041169689Skan	switch (uap->cmd) {
1042169689Skan	case IPC_STAT:
1043169689Skan		ipcperm_new2old(&buf.shm_perm, &old.shm_perm);
104490075Sobrien		if (buf.shm_segsz > INT_MAX)
104590075Sobrien			old.shm_segsz = INT_MAX;
1046169689Skan		else
1047169689Skan			CP(buf, old, shm_segsz);
1048169689Skan		CP(buf, old, shm_lpid);
1049169689Skan		CP(buf, old, shm_cpid);
1050169689Skan		if (buf.shm_nattch > SHRT_MAX)
1051169689Skan			old.shm_nattch = SHRT_MAX;
1052169689Skan		else
1053169689Skan			CP(buf, old, shm_nattch);
1054169689Skan		CP(buf, old, shm_atime);
1055169689Skan		CP(buf, old, shm_dtime);
1056169689Skan		CP(buf, old, shm_ctime);
1057169689Skan		old.shm_internal = NULL;
1058169689Skan		error = copyout(&old, uap->buf, sizeof(old));
1059169689Skan		break;
106090075Sobrien	}
106190075Sobrien
106290075Sobriendone:
106390075Sobrien	if (error) {
106490075Sobrien		/* Invalidate the return value */
106590075Sobrien		td->td_retval[0] = -1;
106690075Sobrien	}
1067169689Skan	return (error);
1068169689Skan}
1069169689Skan
1070169689SkanSYSCALL_MODULE_HELPER(freebsd7_shmctl);
1071169689Skan
1072169689Skan#undef CP
1073169689Skan
1074169689Skan#endif	/* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
107590075Sobrien	   COMPAT_FREEBSD7 */
107690075Sobrien
107790075Sobrienstatic int
1078132718Skansysvshm_modload(struct module *module, int cmd, void *arg)
107990075Sobrien{
108090075Sobrien	int error = 0;
108190075Sobrien
108290075Sobrien	switch (cmd) {
1083117395Skan	case MOD_LOAD:
1084117395Skan		shminit();
108590075Sobrien		break;
108690075Sobrien	case MOD_UNLOAD:
108790075Sobrien		error = shmunload();
1088169689Skan		break;
1089169689Skan	case MOD_SHUTDOWN:
109090075Sobrien		break;
109190075Sobrien	default:
109290075Sobrien		error = EINVAL;
109390075Sobrien		break;
109490075Sobrien	}
109590075Sobrien	return (error);
109690075Sobrien}
109790075Sobrien
109890075Sobrienstatic moduledata_t sysvshm_mod = {
109990075Sobrien	"sysvshm",
1100117395Skan	&sysvshm_modload,
110190075Sobrien	NULL
110290075Sobrien};
110390075Sobrien
110490075SobrienSYSCALL_MODULE_HELPER(shmat);
110590075SobrienSYSCALL_MODULE_HELPER(shmctl);
1106117395SkanSYSCALL_MODULE_HELPER(shmdt);
1107117395SkanSYSCALL_MODULE_HELPER(shmget);
1108117395Skan
1109117395SkanDECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
1110117395SkanMODULE_VERSION(sysvshm, 1);
1111117395Skan