12729Sdfr/*	$NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $	*/
2139804Simp/*-
32729Sdfr * Copyright (c) 1994 Adam Glass and Charles Hannum.  All rights reserved.
42729Sdfr *
52729Sdfr * Redistribution and use in source and binary forms, with or without
62729Sdfr * modification, are permitted provided that the following conditions
72729Sdfr * are met:
82729Sdfr * 1. Redistributions of source code must retain the above copyright
92729Sdfr *    notice, this list of conditions and the following disclaimer.
102729Sdfr * 2. Redistributions in binary form must reproduce the above copyright
112729Sdfr *    notice, this list of conditions and the following disclaimer in the
122729Sdfr *    documentation and/or other materials provided with the distribution.
132729Sdfr * 3. All advertising materials mentioning features or use of this software
142729Sdfr *    must display the following acknowledgement:
152729Sdfr *	This product includes software developed by Adam Glass and Charles
162729Sdfr *	Hannum.
172729Sdfr * 4. The names of the authors may not be used to endorse or promote products
182729Sdfr *    derived from this software without specific prior written permission.
192729Sdfr *
202729Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
212729Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
222729Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
232729Sdfr * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
242729Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
252729Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
262729Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
272729Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
282729Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
292729Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
302729Sdfr */
31140617Srwatson/*-
32140617Srwatson * Copyright (c) 2003-2005 McAfee, Inc.
33140617Srwatson * All rights reserved.
34140617Srwatson *
35140617Srwatson * This software was developed for the FreeBSD Project in part by McAfee
36140617Srwatson * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
37140617Srwatson * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
38140617Srwatson * program.
39140617Srwatson *
40140617Srwatson * Redistribution and use in source and binary forms, with or without
41140617Srwatson * modification, are permitted provided that the following conditions
42140617Srwatson * are met:
43140617Srwatson * 1. Redistributions of source code must retain the above copyright
44140617Srwatson *    notice, this list of conditions and the following disclaimer.
45140617Srwatson * 2. Redistributions in binary form must reproduce the above copyright
46140617Srwatson *    notice, this list of conditions and the following disclaimer in the
47140617Srwatson *    documentation and/or other materials provided with the distribution.
48140617Srwatson *
49140617Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
50140617Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51140617Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52140617Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
53140617Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54140617Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55140617Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56140617Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57140617Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58140617Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59140617Srwatson * SUCH DAMAGE.
60140617Srwatson */
612729Sdfr
62116182Sobrien#include <sys/cdefs.h>
63116182Sobrien__FBSDID("$FreeBSD: stable/11/sys/kern/sysv_shm.c 367601 2020-11-11 22:00:30Z brooks $");
64116182Sobrien
6531778Seivind#include "opt_compat.h"
6658820Speter#include "opt_sysvipc.h"
6713255Swollman
682729Sdfr#include <sys/param.h>
6911626Sbde#include <sys/systm.h>
70360451Sbrooks#include <sys/abi_compat.h>
712729Sdfr#include <sys/kernel.h>
72194910Sjhb#include <sys/limits.h>
7376166Smarkm#include <sys/lock.h>
7458820Speter#include <sys/sysctl.h>
752729Sdfr#include <sys/shm.h>
762729Sdfr#include <sys/proc.h>
772729Sdfr#include <sys/malloc.h>
7876941Sjhb#include <sys/mman.h>
79129882Sphk#include <sys/module.h>
8076827Salfred#include <sys/mutex.h>
81220398Strasz#include <sys/racct.h>
82130730Stjr#include <sys/resourcevar.h>
83248084Sattilio#include <sys/rwlock.h>
842729Sdfr#include <sys/stat.h>
8569449Salfred#include <sys/syscall.h>
86114724Smbr#include <sys/syscallsubr.h>
8711626Sbde#include <sys/sysent.h>
8876166Smarkm#include <sys/sysproto.h>
8968024Srwatson#include <sys/jail.h>
902729Sdfr
91163606Srwatson#include <security/mac/mac_framework.h>
92163606Srwatson
932729Sdfr#include <vm/vm.h>
9412662Sdg#include <vm/vm_param.h>
9512662Sdg#include <vm/pmap.h>
9618098Sdyson#include <vm/vm_object.h>
972729Sdfr#include <vm/vm_map.h>
9842957Sdillon#include <vm/vm_page.h>
9918199Sdyson#include <vm/vm_pager.h>
1002729Sdfr
101219028SnetchildFEATURE(sysv_shm, "System V shared memory segments support");
102219028Snetchild
10330354Sphkstatic MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
10430309Sphk
1052729Sdfr#define	SHMSEG_FREE     	0x0200
1062729Sdfr#define	SHMSEG_REMOVED  	0x0400
1072729Sdfr#define	SHMSEG_ALLOCATED	0x0800
1082729Sdfr
109189283Skibstatic int shm_last_free, shm_nused, shmalloced;
110189398Skibvm_size_t shm_committed;
111298585Sjamiestatic struct shmid_kernel *shmsegs;
112298585Sjamiestatic unsigned shm_prison_slot;
1132729Sdfr
1142729Sdfrstruct shmmap_state {
1152729Sdfr	vm_offset_t va;
1162729Sdfr	int shmid;
1172729Sdfr};
1182729Sdfr
119137613Srwatsonstatic void shm_deallocate_segment(struct shmid_kernel *);
120298585Sjamiestatic int shm_find_segment_by_key(struct prison *, key_t);
121298585Sjamiestatic struct shmid_kernel *shm_find_segment(struct prison *, int, bool);
122109205Sdillonstatic int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *);
123367601Sbrooksstatic int shmget_allocate_segment(struct thread *td, key_t key, size_t size,
124367601Sbrooks    int mode);
125367601Sbrooksstatic int shmget_existing(struct thread *td, size_t size, int shmflg,
126367601Sbrooks    int mode, int segnum);
12792723Salfredstatic void shmrealloc(void);
128205323Skibstatic int shminit(void);
12992723Salfredstatic int sysvshm_modload(struct module *, int, void *);
13092723Salfredstatic int shmunload(void);
131109205Sdillonstatic void shmexit_myhook(struct vmspace *vm);
13292723Salfredstatic void shmfork_myhook(struct proc *p1, struct proc *p2);
13392723Salfredstatic int sysctl_shmsegs(SYSCTL_HANDLER_ARGS);
134298585Sjamiestatic void shm_remove(struct shmid_kernel *, int);
135298585Sjamiestatic struct prison *shm_find_prison(struct ucred *);
136298585Sjamiestatic int shm_prison_cansee(struct prison *, struct shmid_kernel *);
137298585Sjamiestatic int shm_prison_check(void *, void *);
138298585Sjamiestatic int shm_prison_set(void *, void *);
139298585Sjamiestatic int shm_prison_get(void *, void *);
140298585Sjamiestatic int shm_prison_remove(void *, void *);
141298585Sjamiestatic void shm_prison_cleanup(struct prison *);
1422729Sdfr
14358820Speter/*
14477104Sdd * Tuneable values.
14558820Speter */
14658820Speter#ifndef SHMMAXPGS
147209037Sivoras#define	SHMMAXPGS	131072	/* Note: sysv shared memory is swap backed. */
14858820Speter#endif
14958820Speter#ifndef SHMMAX
15058820Speter#define	SHMMAX	(SHMMAXPGS*PAGE_SIZE)
15158820Speter#endif
15258820Speter#ifndef SHMMIN
15358820Speter#define	SHMMIN	1
15458820Speter#endif
15558820Speter#ifndef SHMMNI
15676275Sdillon#define	SHMMNI	192
15758820Speter#endif
15858820Speter#ifndef SHMSEG
15976275Sdillon#define	SHMSEG	128
16058820Speter#endif
16158820Speter#ifndef SHMALL
16258820Speter#define	SHMALL	(SHMMAXPGS)
16358820Speter#endif
16458820Speter
16558820Speterstruct	shminfo shminfo = {
166267992Shselasky	.shmmax = SHMMAX,
167267992Shselasky	.shmmin = SHMMIN,
168267992Shselasky	.shmmni = SHMMNI,
169267992Shselasky	.shmseg = SHMSEG,
170267992Shselasky	.shmall = SHMALL
17158820Speter};
17258820Speter
17361081Sdillonstatic int shm_use_phys;
174289112Straszstatic int shm_allow_removed = 1;
17561081Sdillon
176267992ShselaskySYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RWTUN, &shminfo.shmmax, 0,
177141710Scsjp    "Maximum shared memory segment size");
178267992ShselaskySYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RWTUN, &shminfo.shmmin, 0,
179141710Scsjp    "Minimum shared memory segment size");
180148782ScsjpSYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0,
181141710Scsjp    "Number of shared memory identifiers");
182148782ScsjpSYSCTL_ULONG(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0,
183141710Scsjp    "Number of segments per process");
184267992ShselaskySYSCTL_ULONG(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RWTUN, &shminfo.shmall, 0,
185141710Scsjp    "Maximum number of pages available for shared memory");
186267992ShselaskySYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RWTUN,
187141710Scsjp    &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core");
188267992ShselaskySYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RWTUN,
189141710Scsjp    &shm_allow_removed, 0,
190141710Scsjp    "Enable/Disable attachment to attached segments marked for removal");
191280323SkibSYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLTYPE_OPAQUE | CTLFLAG_RD |
192280323Skib    CTLFLAG_MPSAFE, NULL, 0, sysctl_shmsegs, "",
193329739Sbrooks    "Array of struct shmid_kernel for each potential shared memory segment");
19458820Speter
195280323Skibstatic struct sx sysvshmsx;
196280323Skib#define	SYSVSHM_LOCK()		sx_xlock(&sysvshmsx)
197280323Skib#define	SYSVSHM_UNLOCK()	sx_xunlock(&sysvshmsx)
198280323Skib#define	SYSVSHM_ASSERT_LOCKED()	sx_assert(&sysvshmsx, SA_XLOCKED)
199280323Skib
2002729Sdfrstatic int
201298585Sjamieshm_find_segment_by_key(struct prison *pr, key_t key)
2022729Sdfr{
2032729Sdfr	int i;
2042729Sdfr
20558820Speter	for (i = 0; i < shmalloced; i++)
206137613Srwatson		if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) &&
207298585Sjamie		    shmsegs[i].cred != NULL &&
208298585Sjamie		    shmsegs[i].cred->cr_prison == pr &&
209137613Srwatson		    shmsegs[i].u.shm_perm.key == key)
210101891Salfred			return (i);
211101891Salfred	return (-1);
2122729Sdfr}
2132729Sdfr
214280323Skib/*
215280323Skib * Finds segment either by shmid if is_shmid is true, or by segnum if
216280323Skib * is_shmid is false.
217280323Skib */
218137613Srwatsonstatic struct shmid_kernel *
219298585Sjamieshm_find_segment(struct prison *rpr, int arg, bool is_shmid)
2202729Sdfr{
221280323Skib	struct shmid_kernel *shmseg;
2222729Sdfr	int segnum;
2232729Sdfr
224280323Skib	segnum = is_shmid ? IPCID_TO_IX(arg) : arg;
22558820Speter	if (segnum < 0 || segnum >= shmalloced)
226101891Salfred		return (NULL);
2272729Sdfr	shmseg = &shmsegs[segnum];
228137613Srwatson	if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
229122088Sfjoe	    (!shm_allow_removed &&
230285057Smjg	    (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0) ||
231298585Sjamie	    (is_shmid && shmseg->u.shm_perm.seq != IPCID_TO_SEQ(arg)) ||
232298597Sjamie	    shm_prison_cansee(rpr, shmseg) != 0)
233101891Salfred		return (NULL);
234101891Salfred	return (shmseg);
2352729Sdfr}
2362729Sdfr
2372729Sdfrstatic void
238280323Skibshm_deallocate_segment(struct shmid_kernel *shmseg)
2392729Sdfr{
240189398Skib	vm_size_t size;
2412729Sdfr
242280323Skib	SYSVSHM_ASSERT_LOCKED();
24379224Sdillon
244194910Sjhb	vm_object_deallocate(shmseg->object);
245194910Sjhb	shmseg->object = NULL;
246194910Sjhb	size = round_page(shmseg->u.shm_segsz);
2472729Sdfr	shm_committed -= btoc(size);
2482729Sdfr	shm_nused--;
249137613Srwatson	shmseg->u.shm_perm.mode = SHMSEG_FREE;
250140617Srwatson#ifdef MAC
251172930Srwatson	mac_sysvshm_cleanup(shmseg);
252140617Srwatson#endif
253220398Strasz	racct_sub_cred(shmseg->cred, RACCT_NSHM, 1);
254220398Strasz	racct_sub_cred(shmseg->cred, RACCT_SHMSIZE, size);
255220388Strasz	crfree(shmseg->cred);
256220388Strasz	shmseg->cred = NULL;
2572729Sdfr}
2582729Sdfr
2592729Sdfrstatic int
260109205Sdillonshm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s)
2612729Sdfr{
262137613Srwatson	struct shmid_kernel *shmseg;
2632729Sdfr	int segnum, result;
264189398Skib	vm_size_t size;
2658876Srgrimes
266280323Skib	SYSVSHM_ASSERT_LOCKED();
267280323Skib	segnum = IPCID_TO_IX(shmmap_s->shmid);
268280323Skib	KASSERT(segnum >= 0 && segnum < shmalloced,
269280323Skib	    ("segnum %d shmalloced %d", segnum, shmalloced));
27077104Sdd
2712729Sdfr	shmseg = &shmsegs[segnum];
272194910Sjhb	size = round_page(shmseg->u.shm_segsz);
273109205Sdillon	result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size);
2742729Sdfr	if (result != KERN_SUCCESS)
275101891Salfred		return (EINVAL);
2762729Sdfr	shmmap_s->shmid = -1;
277137613Srwatson	shmseg->u.shm_dtime = time_second;
278137613Srwatson	if ((--shmseg->u.shm_nattch <= 0) &&
279137613Srwatson	    (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) {
2802729Sdfr		shm_deallocate_segment(shmseg);
2812729Sdfr		shm_last_free = segnum;
2822729Sdfr	}
283101891Salfred	return (0);
2842729Sdfr}
2852729Sdfr
286298585Sjamiestatic void
287298585Sjamieshm_remove(struct shmid_kernel *shmseg, int segnum)
288298585Sjamie{
289298585Sjamie
290298585Sjamie	shmseg->u.shm_perm.key = IPC_PRIVATE;
291298585Sjamie	shmseg->u.shm_perm.mode |= SHMSEG_REMOVED;
292298585Sjamie	if (shmseg->u.shm_nattch <= 0) {
293298585Sjamie		shm_deallocate_segment(shmseg);
294298585Sjamie		shm_last_free = segnum;
295298585Sjamie	}
296298585Sjamie}
297298585Sjamie
298298585Sjamiestatic struct prison *
299298585Sjamieshm_find_prison(struct ucred *cred)
300298585Sjamie{
301298585Sjamie	struct prison *pr, *rpr;
302298585Sjamie
303298585Sjamie	pr = cred->cr_prison;
304298585Sjamie	prison_lock(pr);
305298585Sjamie	rpr = osd_jail_get(pr, shm_prison_slot);
306298585Sjamie	prison_unlock(pr);
307298585Sjamie	return rpr;
308298585Sjamie}
309298585Sjamie
310280323Skibstatic int
311298585Sjamieshm_prison_cansee(struct prison *rpr, struct shmid_kernel *shmseg)
312298585Sjamie{
313298585Sjamie
314298585Sjamie	if (shmseg->cred == NULL ||
315298585Sjamie	    !(rpr == shmseg->cred->cr_prison ||
316298585Sjamie	      prison_ischild(rpr, shmseg->cred->cr_prison)))
317298585Sjamie		return (EINVAL);
318298585Sjamie	return (0);
319298585Sjamie}
320298585Sjamie
321298585Sjamiestatic int
322280323Skibkern_shmdt_locked(struct thread *td, const void *shmaddr)
3232729Sdfr{
32483366Sjulian	struct proc *p = td->td_proc;
3252729Sdfr	struct shmmap_state *shmmap_s;
326140617Srwatson#ifdef MAC
327140617Srwatson	struct shmid_kernel *shmsegptr;
328285057Smjg	int error;
329140617Srwatson#endif
330285057Smjg	int i;
3312729Sdfr
332280323Skib	SYSVSHM_ASSERT_LOCKED();
333298585Sjamie	if (shm_find_prison(td->td_ucred) == NULL)
33491703Sjhb		return (ENOSYS);
335100512Salfred	shmmap_s = p->p_vmspace->vm_shm;
336280323Skib 	if (shmmap_s == NULL)
337280323Skib		return (EINVAL);
33882607Sdillon	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) {
3392729Sdfr		if (shmmap_s->shmid != -1 &&
340280323Skib		    shmmap_s->va == (vm_offset_t)shmaddr) {
3412729Sdfr			break;
34282607Sdillon		}
34382607Sdillon	}
344280323Skib	if (i == shminfo.shmseg)
345280323Skib		return (EINVAL);
346140617Srwatson#ifdef MAC
347140617Srwatson	shmsegptr = &shmsegs[IPCID_TO_IX(shmmap_s->shmid)];
348172930Srwatson	error = mac_sysvshm_check_shmdt(td->td_ucred, shmsegptr);
349162468Srwatson	if (error != 0)
350280323Skib		return (error);
351140617Srwatson#endif
352285057Smjg	return (shm_delete_mapping(p->p_vmspace, shmmap_s));
3532729Sdfr}
3542729Sdfr
35512866Speter#ifndef _SYS_SYSPROTO_H_
356280323Skibstruct shmdt_args {
357109831Salfred	const void *shmaddr;
3582729Sdfr};
35912866Speter#endif
3602729Sdfrint
361280323Skibsys_shmdt(struct thread *td, struct shmdt_args *uap)
3622729Sdfr{
363280323Skib	int error;
364280323Skib
365280323Skib	SYSVSHM_LOCK();
366280323Skib	error = kern_shmdt_locked(td, uap->shmaddr);
367280323Skib	SYSVSHM_UNLOCK();
368280323Skib	return (error);
369280323Skib}
370280323Skib
371280323Skibstatic int
372280323Skibkern_shmat_locked(struct thread *td, int shmid, const void *shmaddr,
373280323Skib    int shmflg)
374280323Skib{
375298585Sjamie	struct prison *rpr;
37683366Sjulian	struct proc *p = td->td_proc;
377137613Srwatson	struct shmid_kernel *shmseg;
378281071Skib	struct shmmap_state *shmmap_s;
3792729Sdfr	vm_offset_t attach_va;
3802729Sdfr	vm_prot_t prot;
3812729Sdfr	vm_size_t size;
382343426Skib	int cow, error, find_space, i, rv;
3832729Sdfr
384280323Skib	SYSVSHM_ASSERT_LOCKED();
385298585Sjamie	rpr = shm_find_prison(td->td_ucred);
386298585Sjamie	if (rpr == NULL)
38791703Sjhb		return (ENOSYS);
388100512Salfred	shmmap_s = p->p_vmspace->vm_shm;
3892729Sdfr	if (shmmap_s == NULL) {
390189398Skib		shmmap_s = malloc(shminfo.shmseg * sizeof(struct shmmap_state),
391189398Skib		    M_SHM, M_WAITOK);
392280323Skib		for (i = 0; i < shminfo.shmseg; i++)
393280323Skib			shmmap_s[i].shmid = -1;
394280323Skib		KASSERT(p->p_vmspace->vm_shm == NULL, ("raced"));
395280323Skib		p->p_vmspace->vm_shm = shmmap_s;
3962729Sdfr	}
397298585Sjamie	shmseg = shm_find_segment(rpr, shmid, true);
398280323Skib	if (shmseg == NULL)
399280323Skib		return (EINVAL);
400137613Srwatson	error = ipcperm(td, &shmseg->u.shm_perm,
401114724Smbr	    (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
402280323Skib	if (error != 0)
403280323Skib		return (error);
404140617Srwatson#ifdef MAC
405172930Srwatson	error = mac_sysvshm_check_shmat(td->td_ucred, shmseg, shmflg);
406162468Srwatson	if (error != 0)
407280323Skib		return (error);
408140617Srwatson#endif
4092729Sdfr	for (i = 0; i < shminfo.shmseg; i++) {
4102729Sdfr		if (shmmap_s->shmid == -1)
4112729Sdfr			break;
4122729Sdfr		shmmap_s++;
4132729Sdfr	}
414280323Skib	if (i >= shminfo.shmseg)
415280323Skib		return (EMFILE);
416194910Sjhb	size = round_page(shmseg->u.shm_segsz);
4172729Sdfr	prot = VM_PROT_READ;
418343426Skib	cow = MAP_INHERIT_SHARE | MAP_PREFAULT_PARTIAL;
419114724Smbr	if ((shmflg & SHM_RDONLY) == 0)
4202729Sdfr		prot |= VM_PROT_WRITE;
421280323Skib	if (shmaddr != NULL) {
422280323Skib		if ((shmflg & SHM_RND) != 0)
423298433Spfg			attach_va = rounddown2((vm_offset_t)shmaddr, SHMLBA);
424280323Skib		else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0)
425114724Smbr			attach_va = (vm_offset_t)shmaddr;
426280323Skib		else
427280323Skib			return (EINVAL);
428343426Skib		if ((shmflg & SHM_REMAP) != 0)
429343426Skib			cow |= MAP_REMAP;
430343426Skib		find_space = VMFS_NO_SPACE;
4312729Sdfr	} else {
43277104Sdd		/*
43377104Sdd		 * This is just a hint to vm_map_find() about where to
43477104Sdd		 * put it.
43577104Sdd		 */
436130730Stjr		attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr +
437285056Smjg		    lim_max(td, RLIMIT_DATA));
438343426Skib		find_space = VMFS_OPTIMAL_SPACE;
4392729Sdfr	}
44018098Sdyson
441194910Sjhb	vm_object_reference(shmseg->object);
442270883Salc	rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object, 0, &attach_va,
443343426Skib	    size, 0, find_space, prot, prot, cow);
44418098Sdyson	if (rv != KERN_SUCCESS) {
445194910Sjhb		vm_object_deallocate(shmseg->object);
446280323Skib		return (ENOMEM);
44718098Sdyson	}
44818231Sdyson
4492729Sdfr	shmmap_s->va = attach_va;
450114724Smbr	shmmap_s->shmid = shmid;
451137613Srwatson	shmseg->u.shm_lpid = p->p_pid;
452137613Srwatson	shmseg->u.shm_atime = time_second;
453137613Srwatson	shmseg->u.shm_nattch++;
45483366Sjulian	td->td_retval[0] = attach_va;
45582607Sdillon	return (error);
4562729Sdfr}
4572729Sdfr
458122201Srwatsonint
459280323Skibkern_shmat(struct thread *td, int shmid, const void *shmaddr, int shmflg)
460114724Smbr{
461280323Skib	int error;
462280323Skib
463280323Skib	SYSVSHM_LOCK();
464280323Skib	error = kern_shmat_locked(td, shmid, shmaddr, shmflg);
465280323Skib	SYSVSHM_UNLOCK();
466280323Skib	return (error);
467114724Smbr}
468114724Smbr
469280323Skib#ifndef _SYS_SYSPROTO_H_
470280323Skibstruct shmat_args {
471280323Skib	int shmid;
472280323Skib	const void *shmaddr;
473280323Skib	int shmflg;
474280323Skib};
475280323Skib#endif
4762729Sdfrint
477280323Skibsys_shmat(struct thread *td, struct shmat_args *uap)
4782729Sdfr{
479280323Skib
480280323Skib	return (kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg));
481280323Skib}
482280323Skib
483280323Skibstatic int
484280323Skibkern_shmctl_locked(struct thread *td, int shmid, int cmd, void *buf,
485280323Skib    size_t *bufsz)
486280323Skib{
487298585Sjamie	struct prison *rpr;
488137613Srwatson	struct shmid_kernel *shmseg;
489280323Skib	struct shmid_ds *shmidp;
490280323Skib	struct shm_info shm_info;
491280323Skib	int error;
4922729Sdfr
493280323Skib	SYSVSHM_ASSERT_LOCKED();
494280323Skib
495298585Sjamie	rpr = shm_find_prison(td->td_ucred);
496298585Sjamie	if (rpr == NULL)
49791703Sjhb		return (ENOSYS);
498114724Smbr
499114724Smbr	switch (cmd) {
500176221Scsjp	/*
501176221Scsjp	 * It is possible that kern_shmctl is being called from the Linux ABI
502176221Scsjp	 * layer, in which case, we will need to implement IPC_INFO.  It should
503176221Scsjp	 * be noted that other shmctl calls will be funneled through here for
504176221Scsjp	 * Linix binaries as well.
505176221Scsjp	 *
506176221Scsjp	 * NB: The Linux ABI layer will convert this data to structure(s) more
507176221Scsjp	 * consistent with the Linux ABI.
508176221Scsjp	 */
50985623Smr	case IPC_INFO:
510114724Smbr		memcpy(buf, &shminfo, sizeof(shminfo));
511114724Smbr		if (bufsz)
512114724Smbr			*bufsz = sizeof(shminfo);
51385623Smr		td->td_retval[0] = shmalloced;
514280323Skib		return (0);
51585623Smr	case SHM_INFO: {
51685623Smr		shm_info.used_ids = shm_nused;
51785623Smr		shm_info.shm_rss = 0;	/*XXX where to get from ? */
51885623Smr		shm_info.shm_tot = 0;	/*XXX where to get from ? */
51985623Smr		shm_info.shm_swp = 0;	/*XXX where to get from ? */
52085623Smr		shm_info.swap_attempts = 0;	/*XXX where to get from ? */
52185623Smr		shm_info.swap_successes = 0;	/*XXX where to get from ? */
522114724Smbr		memcpy(buf, &shm_info, sizeof(shm_info));
523280323Skib		if (bufsz != NULL)
524114724Smbr			*bufsz = sizeof(shm_info);
52585623Smr		td->td_retval[0] = shmalloced;
526280323Skib		return (0);
52785623Smr	}
52885623Smr	}
529298585Sjamie	shmseg = shm_find_segment(rpr, shmid, cmd != SHM_STAT);
530280323Skib	if (shmseg == NULL)
531280323Skib		return (EINVAL);
532140617Srwatson#ifdef MAC
533172930Srwatson	error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd);
534162468Srwatson	if (error != 0)
535280323Skib		return (error);
536140617Srwatson#endif
537114724Smbr	switch (cmd) {
53885623Smr	case SHM_STAT:
5392729Sdfr	case IPC_STAT:
540298585Sjamie		shmidp = (struct shmid_ds *)buf;
541137613Srwatson		error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
542280323Skib		if (error != 0)
543280323Skib			return (error);
544298585Sjamie		memcpy(shmidp, &shmseg->u, sizeof(struct shmid_ds));
545298585Sjamie		if (td->td_ucred->cr_prison != shmseg->cred->cr_prison)
546298585Sjamie			shmidp->shm_perm.key = IPC_PRIVATE;
547280323Skib		if (bufsz != NULL)
548114724Smbr			*bufsz = sizeof(struct shmid_ds);
549280323Skib		if (cmd == SHM_STAT) {
550280323Skib			td->td_retval[0] = IXSEQ_TO_IPCID(shmid,
551280323Skib			    shmseg->u.shm_perm);
552280323Skib		}
5532729Sdfr		break;
554280323Skib	case IPC_SET:
555280323Skib		shmidp = (struct shmid_ds *)buf;
556137613Srwatson		error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
557280323Skib		if (error != 0)
558280323Skib			return (error);
559280323Skib		shmseg->u.shm_perm.uid = shmidp->shm_perm.uid;
560280323Skib		shmseg->u.shm_perm.gid = shmidp->shm_perm.gid;
561137613Srwatson		shmseg->u.shm_perm.mode =
562137613Srwatson		    (shmseg->u.shm_perm.mode & ~ACCESSPERMS) |
563280323Skib		    (shmidp->shm_perm.mode & ACCESSPERMS);
564137613Srwatson		shmseg->u.shm_ctime = time_second;
5652729Sdfr		break;
5662729Sdfr	case IPC_RMID:
567137613Srwatson		error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
568280323Skib		if (error != 0)
569280323Skib			return (error);
570298585Sjamie		shm_remove(shmseg, IPCID_TO_IX(shmid));
5712729Sdfr		break;
5722729Sdfr#if 0
5732729Sdfr	case SHM_LOCK:
5742729Sdfr	case SHM_UNLOCK:
5752729Sdfr#endif
5762729Sdfr	default:
57782607Sdillon		error = EINVAL;
57882607Sdillon		break;
5792729Sdfr	}
58082607Sdillon	return (error);
5812729Sdfr}
5822729Sdfr
583280323Skibint
584280323Skibkern_shmctl(struct thread *td, int shmid, int cmd, void *buf, size_t *bufsz)
585280323Skib{
586280323Skib	int error;
587280323Skib
588280323Skib	SYSVSHM_LOCK();
589280323Skib	error = kern_shmctl_locked(td, shmid, cmd, buf, bufsz);
590280323Skib	SYSVSHM_UNLOCK();
591280323Skib	return (error);
592280323Skib}
593280323Skib
594280323Skib
595194832Sjhb#ifndef _SYS_SYSPROTO_H_
596194832Sjhbstruct shmctl_args {
597194832Sjhb	int shmid;
598194832Sjhb	int cmd;
599194832Sjhb	struct shmid_ds *buf;
600194832Sjhb};
601194832Sjhb#endif
602114724Smbrint
603280323Skibsys_shmctl(struct thread *td, struct shmctl_args *uap)
604114724Smbr{
605285057Smjg	int error;
606114724Smbr	struct shmid_ds buf;
607114724Smbr	size_t bufsz;
608285057Smjg
609176221Scsjp	/*
610176221Scsjp	 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
611176221Scsjp	 * Linux binaries.  If we see the call come through the FreeBSD ABI,
612176221Scsjp	 * return an error back to the user since we do not to support this.
613176221Scsjp	 */
614176221Scsjp	if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
615176221Scsjp	    uap->cmd == SHM_STAT)
616176221Scsjp		return (EINVAL);
617176221Scsjp
618114724Smbr	/* IPC_SET needs to copyin the buffer before calling kern_shmctl */
619114724Smbr	if (uap->cmd == IPC_SET) {
620114724Smbr		if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds))))
621114724Smbr			goto done;
622114724Smbr	}
623285057Smjg
624122088Sfjoe	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
625114724Smbr	if (error)
626114724Smbr		goto done;
627285057Smjg
628114724Smbr	/* Cases in which we need to copyout */
629114724Smbr	switch (uap->cmd) {
630114724Smbr	case IPC_STAT:
631114724Smbr		error = copyout(&buf, uap->buf, bufsz);
632114724Smbr		break;
633114724Smbr	}
634114724Smbr
635114724Smbrdone:
636114724Smbr	if (error) {
637114724Smbr		/* Invalidate the return value */
638114724Smbr		td->td_retval[0] = -1;
639114724Smbr	}
640114724Smbr	return (error);
641114724Smbr}
642114724Smbr
643114724Smbr
6442729Sdfrstatic int
645367601Sbrooksshmget_existing(struct thread *td, size_t size, int shmflg, int mode,
646280323Skib    int segnum)
6472729Sdfr{
648137613Srwatson	struct shmid_kernel *shmseg;
649280325Scognet#ifdef MAC
6502729Sdfr	int error;
651280325Scognet#endif
6522729Sdfr
653280323Skib	SYSVSHM_ASSERT_LOCKED();
654280323Skib	KASSERT(segnum >= 0 && segnum < shmalloced,
655280323Skib	    ("segnum %d shmalloced %d", segnum, shmalloced));
6562729Sdfr	shmseg = &shmsegs[segnum];
657367601Sbrooks	if ((shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
658101891Salfred		return (EEXIST);
659140617Srwatson#ifdef MAC
660367601Sbrooks	error = mac_sysvshm_check_shmget(td->td_ucred, shmseg, shmflg);
661162468Srwatson	if (error != 0)
662150937Srwatson		return (error);
663140617Srwatson#endif
664367601Sbrooks	if (size != 0 && size > shmseg->u.shm_segsz)
665101891Salfred		return (EINVAL);
666137613Srwatson	td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
667101891Salfred	return (0);
6682729Sdfr}
6692729Sdfr
6702729Sdfrstatic int
671367601Sbrooksshmget_allocate_segment(struct thread *td, key_t key, size_t size, int mode)
6722729Sdfr{
67391406Sjhb	struct ucred *cred = td->td_ucred;
674137613Srwatson	struct shmid_kernel *shmseg;
675131857Salc	vm_object_t shm_object;
676280323Skib	int i, segnum;
6778876Srgrimes
678280323Skib	SYSVSHM_ASSERT_LOCKED();
67979224Sdillon
680367601Sbrooks	if (size < shminfo.shmmin || size > shminfo.shmmax)
681101891Salfred		return (EINVAL);
68277104Sdd	if (shm_nused >= shminfo.shmmni) /* Any shmids left? */
683101891Salfred		return (ENOSPC);
684367601Sbrooks	size = round_page(size);
6852729Sdfr	if (shm_committed + btoc(size) > shminfo.shmall)
686101891Salfred		return (ENOMEM);
6872729Sdfr	if (shm_last_free < 0) {
68877104Sdd		shmrealloc();	/* Maybe expand the shmsegs[] array. */
68958820Speter		for (i = 0; i < shmalloced; i++)
690137613Srwatson			if (shmsegs[i].u.shm_perm.mode & SHMSEG_FREE)
6912729Sdfr				break;
69258820Speter		if (i == shmalloced)
693101891Salfred			return (ENOSPC);
6942729Sdfr		segnum = i;
6952729Sdfr	} else  {
6962729Sdfr		segnum = shm_last_free;
6972729Sdfr		shm_last_free = -1;
6982729Sdfr	}
699280323Skib	KASSERT(segnum >= 0 && segnum < shmalloced,
700280323Skib	    ("segnum %d shmalloced %d", segnum, shmalloced));
7012729Sdfr	shmseg = &shmsegs[segnum];
702223825Strasz#ifdef RACCT
703282213Strasz	if (racct_enable) {
704282213Strasz		PROC_LOCK(td->td_proc);
705282213Strasz		if (racct_add(td->td_proc, RACCT_NSHM, 1)) {
706282213Strasz			PROC_UNLOCK(td->td_proc);
707282213Strasz			return (ENOSPC);
708282213Strasz		}
709282213Strasz		if (racct_add(td->td_proc, RACCT_SHMSIZE, size)) {
710282213Strasz			racct_sub(td->td_proc, RACCT_NSHM, 1);
711282213Strasz			PROC_UNLOCK(td->td_proc);
712282213Strasz			return (ENOMEM);
713282213Strasz		}
714220398Strasz		PROC_UNLOCK(td->td_proc);
715220398Strasz	}
716223825Strasz#endif
717280323Skib
7182729Sdfr	/*
71918199Sdyson	 * We make sure that we have allocated a pager before we need
72018199Sdyson	 * to.
72118199Sdyson	 */
722194766Skib	shm_object = vm_pager_allocate(shm_use_phys ? OBJT_PHYS : OBJT_SWAP,
723194766Skib	    0, size, VM_PROT_DEFAULT, 0, cred);
724220398Strasz	if (shm_object == NULL) {
725223825Strasz#ifdef RACCT
726282213Strasz		if (racct_enable) {
727282213Strasz			PROC_LOCK(td->td_proc);
728282213Strasz			racct_sub(td->td_proc, RACCT_NSHM, 1);
729282213Strasz			racct_sub(td->td_proc, RACCT_SHMSIZE, size);
730282213Strasz			PROC_UNLOCK(td->td_proc);
731282213Strasz		}
732223825Strasz#endif
733194766Skib		return (ENOMEM);
734220398Strasz	}
735278697Salc	shm_object->pg_color = 0;
736248084Sattilio	VM_OBJECT_WLOCK(shm_object);
737131857Salc	vm_object_clear_flag(shm_object, OBJ_ONEMAPPING);
738278697Salc	vm_object_set_flag(shm_object, OBJ_COLORED | OBJ_NOSPLIT);
739248084Sattilio	VM_OBJECT_WUNLOCK(shm_object);
74035669Sdyson
741194910Sjhb	shmseg->object = shm_object;
742137613Srwatson	shmseg->u.shm_perm.cuid = shmseg->u.shm_perm.uid = cred->cr_uid;
743137613Srwatson	shmseg->u.shm_perm.cgid = shmseg->u.shm_perm.gid = cred->cr_gid;
744280323Skib	shmseg->u.shm_perm.mode = (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
745367601Sbrooks	shmseg->u.shm_perm.key = key;
746280323Skib	shmseg->u.shm_perm.seq = (shmseg->u.shm_perm.seq + 1) & 0x7fff;
747220399Strasz	shmseg->cred = crhold(cred);
748367601Sbrooks	shmseg->u.shm_segsz = size;
749137613Srwatson	shmseg->u.shm_cpid = td->td_proc->p_pid;
750137613Srwatson	shmseg->u.shm_lpid = shmseg->u.shm_nattch = 0;
751137613Srwatson	shmseg->u.shm_atime = shmseg->u.shm_dtime = 0;
752140617Srwatson#ifdef MAC
753172930Srwatson	mac_sysvshm_create(cred, shmseg);
754140617Srwatson#endif
755137613Srwatson	shmseg->u.shm_ctime = time_second;
7562729Sdfr	shm_committed += btoc(size);
7572729Sdfr	shm_nused++;
758280323Skib	td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
759280323Skib
760101891Salfred	return (0);
7612729Sdfr}
7622729Sdfr
763194832Sjhb#ifndef _SYS_SYSPROTO_H_
764194832Sjhbstruct shmget_args {
765194832Sjhb	key_t key;
766194832Sjhb	size_t size;
767194832Sjhb	int shmflg;
768194832Sjhb};
769194832Sjhb#endif
7702729Sdfrint
771280323Skibsys_shmget(struct thread *td, struct shmget_args *uap)
7722729Sdfr{
77382607Sdillon	int segnum, mode;
77482607Sdillon	int error;
7752729Sdfr
776298585Sjamie	if (shm_find_prison(td->td_ucred) == NULL)
77791703Sjhb		return (ENOSYS);
7782729Sdfr	mode = uap->shmflg & ACCESSPERMS;
779280323Skib	SYSVSHM_LOCK();
780280323Skib	if (uap->key == IPC_PRIVATE) {
781367601Sbrooks		error = shmget_allocate_segment(td, uap->key, uap->size, mode);
782280323Skib	} else {
783298585Sjamie		segnum = shm_find_segment_by_key(td->td_ucred->cr_prison,
784298585Sjamie		    uap->key);
785280323Skib		if (segnum >= 0)
786367601Sbrooks			error = shmget_existing(td, uap->size, uap->shmflg,
787367601Sbrooks			    mode, segnum);
788280323Skib		else if ((uap->shmflg & IPC_CREAT) == 0)
78982607Sdillon			error = ENOENT;
790280323Skib		else
791367601Sbrooks			error = shmget_allocate_segment(td, uap->key,
792367601Sbrooks			    uap->size, mode);
7932729Sdfr	}
794280323Skib	SYSVSHM_UNLOCK();
79582607Sdillon	return (error);
7962729Sdfr}
7972729Sdfr
79869449Salfredstatic void
799280323Skibshmfork_myhook(struct proc *p1, struct proc *p2)
8002729Sdfr{
8012729Sdfr	struct shmmap_state *shmmap_s;
8022729Sdfr	size_t size;
8032729Sdfr	int i;
8042729Sdfr
805280323Skib	SYSVSHM_LOCK();
8062729Sdfr	size = shminfo.shmseg * sizeof(struct shmmap_state);
807111119Simp	shmmap_s = malloc(size, M_SHM, M_WAITOK);
808100511Salfred	bcopy(p1->p_vmspace->vm_shm, shmmap_s, size);
809100511Salfred	p2->p_vmspace->vm_shm = shmmap_s;
810280323Skib	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) {
811280323Skib		if (shmmap_s->shmid != -1) {
812280323Skib			KASSERT(IPCID_TO_IX(shmmap_s->shmid) >= 0 &&
813280323Skib			    IPCID_TO_IX(shmmap_s->shmid) < shmalloced,
814280323Skib			    ("segnum %d shmalloced %d",
815280323Skib			    IPCID_TO_IX(shmmap_s->shmid), shmalloced));
816137613Srwatson			shmsegs[IPCID_TO_IX(shmmap_s->shmid)].u.shm_nattch++;
817280323Skib		}
818280323Skib	}
819280323Skib	SYSVSHM_UNLOCK();
8202729Sdfr}
8212729Sdfr
82269449Salfredstatic void
823109205Sdillonshmexit_myhook(struct vmspace *vm)
8242729Sdfr{
825109205Sdillon	struct shmmap_state *base, *shm;
8262729Sdfr	int i;
8272729Sdfr
828280323Skib	base = vm->vm_shm;
829280323Skib	if (base != NULL) {
830109205Sdillon		vm->vm_shm = NULL;
831280323Skib		SYSVSHM_LOCK();
832109205Sdillon		for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) {
833109205Sdillon			if (shm->shmid != -1)
834109205Sdillon				shm_delete_mapping(vm, shm);
835109205Sdillon		}
836280323Skib		SYSVSHM_UNLOCK();
837109205Sdillon		free(base, M_SHM);
838109205Sdillon	}
8392729Sdfr}
8402729Sdfr
84158820Speterstatic void
84258820Spetershmrealloc(void)
84358820Speter{
844280323Skib	struct shmid_kernel *newsegs;
84558820Speter	int i;
84658820Speter
847280323Skib	SYSVSHM_ASSERT_LOCKED();
848280323Skib
84958820Speter	if (shmalloced >= shminfo.shmmni)
85058820Speter		return;
85158820Speter
852329177Sbrooks	newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM,
853329177Sbrooks	    M_WAITOK | M_ZERO);
85458820Speter	for (i = 0; i < shmalloced; i++)
85558820Speter		bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
85658820Speter	for (; i < shminfo.shmmni; i++) {
857285055Smjg		newsegs[i].u.shm_perm.mode = SHMSEG_FREE;
858285055Smjg		newsegs[i].u.shm_perm.seq = 0;
859140617Srwatson#ifdef MAC
860285055Smjg		mac_sysvshm_init(&newsegs[i]);
861140617Srwatson#endif
86258820Speter	}
86358820Speter	free(shmsegs, M_SHM);
86458820Speter	shmsegs = newsegs;
86558820Speter	shmalloced = shminfo.shmmni;
86658820Speter}
86758820Speter
868205323Skibstatic struct syscall_helper_data shm_syscalls[] = {
869205323Skib	SYSCALL_INIT_HELPER(shmat),
870205323Skib	SYSCALL_INIT_HELPER(shmctl),
871205323Skib	SYSCALL_INIT_HELPER(shmdt),
872205323Skib	SYSCALL_INIT_HELPER(shmget),
873205323Skib#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
874205323Skib    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
875225617Skmacy	SYSCALL_INIT_HELPER_COMPAT(freebsd7_shmctl),
876205323Skib#endif
877205323Skib#if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
878205323Skib	SYSCALL_INIT_HELPER(shmsys),
879205323Skib#endif
880205323Skib	SYSCALL_INIT_LAST
881205323Skib};
882205323Skib
883205323Skib#ifdef COMPAT_FREEBSD32
884205323Skib#include <compat/freebsd32/freebsd32.h>
885205323Skib#include <compat/freebsd32/freebsd32_ipc.h>
886205323Skib#include <compat/freebsd32/freebsd32_proto.h>
887205323Skib#include <compat/freebsd32/freebsd32_signal.h>
888205323Skib#include <compat/freebsd32/freebsd32_syscall.h>
889205323Skib#include <compat/freebsd32/freebsd32_util.h>
890205323Skib
891205323Skibstatic struct syscall_helper_data shm32_syscalls[] = {
892225617Skmacy	SYSCALL32_INIT_HELPER_COMPAT(shmat),
893225617Skmacy	SYSCALL32_INIT_HELPER_COMPAT(shmdt),
894225617Skmacy	SYSCALL32_INIT_HELPER_COMPAT(shmget),
895205323Skib	SYSCALL32_INIT_HELPER(freebsd32_shmsys),
896205323Skib	SYSCALL32_INIT_HELPER(freebsd32_shmctl),
897205323Skib#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
898205323Skib    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
899205323Skib	SYSCALL32_INIT_HELPER(freebsd7_freebsd32_shmctl),
900205323Skib#endif
901205323Skib	SYSCALL_INIT_LAST
902205323Skib};
903205323Skib#endif
904205323Skib
905205323Skibstatic int
906280323Skibshminit(void)
9072729Sdfr{
908298585Sjamie	struct prison *pr;
909298661Scem	void **rsv;
910205323Skib	int i, error;
911298585Sjamie	osd_method_t methods[PR_MAXMETHOD] = {
912298585Sjamie	    [PR_METHOD_CHECK] =		shm_prison_check,
913298585Sjamie	    [PR_METHOD_SET] =		shm_prison_set,
914298585Sjamie	    [PR_METHOD_GET] =		shm_prison_get,
915298585Sjamie	    [PR_METHOD_REMOVE] =	shm_prison_remove,
916298585Sjamie	};
91758820Speter
918198449Sru#ifndef BURN_BRIDGES
919198449Sru	if (TUNABLE_ULONG_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall) != 0)
920198449Sru		printf("kern.ipc.shmmaxpgs is now called kern.ipc.shmall!\n");
921198449Sru#endif
922267992Shselasky	if (shminfo.shmmax == SHMMAX) {
923231195Spjd		/* Initialize shmmax dealing with possible overflow. */
924267992Shselasky		for (i = PAGE_SIZE; i != 0; i--) {
925231195Spjd			shminfo.shmmax = shminfo.shmall * i;
926267992Shselasky			if ((shminfo.shmmax / shminfo.shmall) == (u_long)i)
927231195Spjd				break;
928231195Spjd		}
929110982Salfred	}
93058820Speter	shmalloced = shminfo.shmmni;
931329177Sbrooks	shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM,
932329177Sbrooks	    M_WAITOK|M_ZERO);
93358820Speter	for (i = 0; i < shmalloced; i++) {
934137613Srwatson		shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
935137613Srwatson		shmsegs[i].u.shm_perm.seq = 0;
936140617Srwatson#ifdef MAC
937172930Srwatson		mac_sysvshm_init(&shmsegs[i]);
938140617Srwatson#endif
9392729Sdfr	}
9402729Sdfr	shm_last_free = 0;
9412729Sdfr	shm_nused = 0;
9422729Sdfr	shm_committed = 0;
943280323Skib	sx_init(&sysvshmsx, "sysvshmsx");
94469449Salfred	shmexit_hook = &shmexit_myhook;
94569449Salfred	shmfork_hook = &shmfork_myhook;
946205323Skib
947298585Sjamie	/* Set current prisons according to their allow.sysvipc. */
948298585Sjamie	shm_prison_slot = osd_jail_register(NULL, methods);
949298585Sjamie	rsv = osd_reserve(shm_prison_slot);
950298585Sjamie	prison_lock(&prison0);
951298585Sjamie	(void)osd_jail_set_reserved(&prison0, shm_prison_slot, rsv, &prison0);
952298585Sjamie	prison_unlock(&prison0);
953298585Sjamie	rsv = NULL;
954298585Sjamie	sx_slock(&allprison_lock);
955298585Sjamie	TAILQ_FOREACH(pr, &allprison, pr_list) {
956298585Sjamie		if (rsv == NULL)
957298585Sjamie			rsv = osd_reserve(shm_prison_slot);
958298585Sjamie		prison_lock(pr);
959298585Sjamie		if ((pr->pr_allow & PR_ALLOW_SYSVIPC) && pr->pr_ref > 0) {
960298585Sjamie			(void)osd_jail_set_reserved(pr, shm_prison_slot, rsv,
961298585Sjamie			    &prison0);
962298585Sjamie			rsv = NULL;
963298585Sjamie		}
964298585Sjamie		prison_unlock(pr);
965298585Sjamie	}
966298585Sjamie	if (rsv != NULL)
967298585Sjamie		osd_free_reserved(rsv);
968298585Sjamie	sx_sunlock(&allprison_lock);
969298585Sjamie
970273707Smjg	error = syscall_helper_register(shm_syscalls, SY_THR_STATIC_KLD);
971205323Skib	if (error != 0)
972205323Skib		return (error);
973205323Skib#ifdef COMPAT_FREEBSD32
974273707Smjg	error = syscall32_helper_register(shm32_syscalls, SY_THR_STATIC_KLD);
975205323Skib	if (error != 0)
976205323Skib		return (error);
977205323Skib#endif
978205323Skib	return (0);
9792729Sdfr}
98069449Salfred
98169449Salfredstatic int
982280323Skibshmunload(void)
98369449Salfred{
984285057Smjg	int i;
98569449Salfred
98669449Salfred	if (shm_nused > 0)
98769449Salfred		return (EBUSY);
98869449Salfred
989205323Skib#ifdef COMPAT_FREEBSD32
990205323Skib	syscall32_helper_unregister(shm32_syscalls);
991205323Skib#endif
992205323Skib	syscall_helper_unregister(shm_syscalls);
993298585Sjamie	if (shm_prison_slot != 0)
994298585Sjamie		osd_jail_deregister(shm_prison_slot);
995205323Skib
996209580Skib	for (i = 0; i < shmalloced; i++) {
997140617Srwatson#ifdef MAC
998172930Srwatson		mac_sysvshm_destroy(&shmsegs[i]);
999140617Srwatson#endif
1000209580Skib		/*
1001209580Skib		 * Objects might be still mapped into the processes
1002209580Skib		 * address spaces.  Actual free would happen on the
1003209580Skib		 * last mapping destruction.
1004209580Skib		 */
1005209580Skib		if (shmsegs[i].u.shm_perm.mode != SHMSEG_FREE)
1006209580Skib			vm_object_deallocate(shmsegs[i].object);
1007209580Skib	}
100869449Salfred	free(shmsegs, M_SHM);
100969449Salfred	shmexit_hook = NULL;
101069449Salfred	shmfork_hook = NULL;
1011280323Skib	sx_destroy(&sysvshmsx);
101269449Salfred	return (0);
101369449Salfred}
101469449Salfred
101569449Salfredstatic int
101677461Sddsysctl_shmsegs(SYSCTL_HANDLER_ARGS)
101777461Sdd{
1018298656Sjamie	struct shmid_kernel tshmseg;
1019329177Sbrooks#ifdef COMPAT_FREEBSD32
1020329177Sbrooks	struct shmid_kernel32 tshmseg32;
1021329177Sbrooks#endif
1022298656Sjamie	struct prison *pr, *rpr;
1023329177Sbrooks	void *outaddr;
1024329177Sbrooks	size_t outsize;
1025298585Sjamie	int error, i;
102677461Sdd
1027280323Skib	SYSVSHM_LOCK();
1028298656Sjamie	pr = req->td->td_ucred->cr_prison;
1029298585Sjamie	rpr = shm_find_prison(req->td->td_ucred);
1030298656Sjamie	error = 0;
1031298585Sjamie	for (i = 0; i < shmalloced; i++) {
1032298656Sjamie		if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
1033298585Sjamie		    rpr == NULL || shm_prison_cansee(rpr, &shmsegs[i]) != 0) {
1034298656Sjamie			bzero(&tshmseg, sizeof(tshmseg));
1035298656Sjamie			tshmseg.u.shm_perm.mode = SHMSEG_FREE;
1036298656Sjamie		} else {
1037298656Sjamie			tshmseg = shmsegs[i];
1038298656Sjamie			if (tshmseg.cred->cr_prison != pr)
1039298656Sjamie				tshmseg.u.shm_perm.key = IPC_PRIVATE;
1040298585Sjamie		}
1041329177Sbrooks#ifdef COMPAT_FREEBSD32
1042329177Sbrooks		if (SV_CURPROC_FLAG(SV_ILP32)) {
1043329177Sbrooks			bzero(&tshmseg32, sizeof(tshmseg32));
1044329177Sbrooks			freebsd32_ipcperm_out(&tshmseg.u.shm_perm,
1045329177Sbrooks			    &tshmseg32.u.shm_perm);
1046329177Sbrooks			CP(tshmseg, tshmseg32, u.shm_segsz);
1047329177Sbrooks			CP(tshmseg, tshmseg32, u.shm_lpid);
1048329177Sbrooks			CP(tshmseg, tshmseg32, u.shm_cpid);
1049329177Sbrooks			CP(tshmseg, tshmseg32, u.shm_nattch);
1050329177Sbrooks			CP(tshmseg, tshmseg32, u.shm_atime);
1051329177Sbrooks			CP(tshmseg, tshmseg32, u.shm_dtime);
1052329177Sbrooks			CP(tshmseg, tshmseg32, u.shm_ctime);
1053329177Sbrooks			/* Don't copy object, label, or cred */
1054329177Sbrooks			outaddr = &tshmseg32;
1055329177Sbrooks			outsize = sizeof(tshmseg32);
1056329177Sbrooks		} else
1057329177Sbrooks#endif
1058329177Sbrooks		{
1059329177Sbrooks			tshmseg.object = NULL;
1060329177Sbrooks			tshmseg.label = NULL;
1061329177Sbrooks			tshmseg.cred = NULL;
1062329177Sbrooks			outaddr = &tshmseg;
1063329177Sbrooks			outsize = sizeof(tshmseg);
1064329177Sbrooks		}
1065329177Sbrooks		error = SYSCTL_OUT(req, outaddr, outsize);
1066298656Sjamie		if (error != 0)
1067298656Sjamie			break;
1068298585Sjamie	}
1069280323Skib	SYSVSHM_UNLOCK();
1070280323Skib	return (error);
107177461Sdd}
107277461Sdd
1073298585Sjamiestatic int
1074298585Sjamieshm_prison_check(void *obj, void *data)
1075298585Sjamie{
1076298585Sjamie	struct prison *pr = obj;
1077298585Sjamie	struct prison *prpr;
1078298585Sjamie	struct vfsoptlist *opts = data;
1079298585Sjamie	int error, jsys;
1080298585Sjamie
1081298585Sjamie	/*
1082298585Sjamie	 * sysvshm is a jailsys integer.
1083298585Sjamie	 * It must be "disable" if the parent jail is disabled.
1084298585Sjamie	 */
1085298585Sjamie	error = vfs_copyopt(opts, "sysvshm", &jsys, sizeof(jsys));
1086298585Sjamie	if (error != ENOENT) {
1087298585Sjamie		if (error != 0)
1088298585Sjamie			return (error);
1089298585Sjamie		switch (jsys) {
1090298585Sjamie		case JAIL_SYS_DISABLE:
1091298585Sjamie			break;
1092298585Sjamie		case JAIL_SYS_NEW:
1093298585Sjamie		case JAIL_SYS_INHERIT:
1094298585Sjamie			prison_lock(pr->pr_parent);
1095298585Sjamie			prpr = osd_jail_get(pr->pr_parent, shm_prison_slot);
1096298585Sjamie			prison_unlock(pr->pr_parent);
1097298585Sjamie			if (prpr == NULL)
1098298585Sjamie				return (EPERM);
1099298585Sjamie			break;
1100298585Sjamie		default:
1101298585Sjamie			return (EINVAL);
1102298585Sjamie		}
1103298585Sjamie	}
1104298585Sjamie
1105298585Sjamie	return (0);
1106298585Sjamie}
1107298585Sjamie
1108298585Sjamiestatic int
1109298585Sjamieshm_prison_set(void *obj, void *data)
1110298585Sjamie{
1111298585Sjamie	struct prison *pr = obj;
1112298585Sjamie	struct prison *tpr, *orpr, *nrpr, *trpr;
1113298585Sjamie	struct vfsoptlist *opts = data;
1114298585Sjamie	void *rsv;
1115298585Sjamie	int jsys, descend;
1116298585Sjamie
1117298585Sjamie	/*
1118298585Sjamie	 * sysvshm controls which jail is the root of the associated segments
1119298585Sjamie	 * (this jail or same as the parent), or if the feature is available
1120298585Sjamie	 * at all.
1121298585Sjamie	 */
1122298585Sjamie	if (vfs_copyopt(opts, "sysvshm", &jsys, sizeof(jsys)) == ENOENT)
1123298585Sjamie		jsys = vfs_flagopt(opts, "allow.sysvipc", NULL, 0)
1124298585Sjamie		    ? JAIL_SYS_INHERIT
1125298585Sjamie		    : vfs_flagopt(opts, "allow.nosysvipc", NULL, 0)
1126298585Sjamie		    ? JAIL_SYS_DISABLE
1127298585Sjamie		    : -1;
1128298585Sjamie	if (jsys == JAIL_SYS_DISABLE) {
1129298585Sjamie		prison_lock(pr);
1130298585Sjamie		orpr = osd_jail_get(pr, shm_prison_slot);
1131298585Sjamie		if (orpr != NULL)
1132298585Sjamie			osd_jail_del(pr, shm_prison_slot);
1133298585Sjamie		prison_unlock(pr);
1134298585Sjamie		if (orpr != NULL) {
1135298585Sjamie			if (orpr == pr)
1136298585Sjamie				shm_prison_cleanup(pr);
1137298585Sjamie			/* Disable all child jails as well. */
1138298585Sjamie			FOREACH_PRISON_DESCENDANT(pr, tpr, descend) {
1139298585Sjamie				prison_lock(tpr);
1140298585Sjamie				trpr = osd_jail_get(tpr, shm_prison_slot);
1141298585Sjamie				if (trpr != NULL) {
1142298585Sjamie					osd_jail_del(tpr, shm_prison_slot);
1143298585Sjamie					prison_unlock(tpr);
1144298585Sjamie					if (trpr == tpr)
1145298585Sjamie						shm_prison_cleanup(tpr);
1146298585Sjamie				} else {
1147298585Sjamie					prison_unlock(tpr);
1148298585Sjamie					descend = 0;
1149298585Sjamie				}
1150298585Sjamie			}
1151298585Sjamie		}
1152298585Sjamie	} else if (jsys != -1) {
1153298585Sjamie		if (jsys == JAIL_SYS_NEW)
1154298585Sjamie			nrpr = pr;
1155298585Sjamie		else {
1156298585Sjamie			prison_lock(pr->pr_parent);
1157298585Sjamie			nrpr = osd_jail_get(pr->pr_parent, shm_prison_slot);
1158298585Sjamie			prison_unlock(pr->pr_parent);
1159298585Sjamie		}
1160298585Sjamie		rsv = osd_reserve(shm_prison_slot);
1161298585Sjamie		prison_lock(pr);
1162298585Sjamie		orpr = osd_jail_get(pr, shm_prison_slot);
1163298585Sjamie		if (orpr != nrpr)
1164298585Sjamie			(void)osd_jail_set_reserved(pr, shm_prison_slot, rsv,
1165298585Sjamie			    nrpr);
1166298585Sjamie		else
1167298585Sjamie			osd_free_reserved(rsv);
1168298585Sjamie		prison_unlock(pr);
1169298585Sjamie		if (orpr != nrpr) {
1170298585Sjamie			if (orpr == pr)
1171298585Sjamie				shm_prison_cleanup(pr);
1172298585Sjamie			if (orpr != NULL) {
1173298585Sjamie				/* Change child jails matching the old root, */
1174298585Sjamie				FOREACH_PRISON_DESCENDANT(pr, tpr, descend) {
1175298585Sjamie					prison_lock(tpr);
1176298585Sjamie					trpr = osd_jail_get(tpr,
1177298585Sjamie					    shm_prison_slot);
1178298585Sjamie					if (trpr == orpr) {
1179298585Sjamie						(void)osd_jail_set(tpr,
1180298585Sjamie						    shm_prison_slot, nrpr);
1181298585Sjamie						prison_unlock(tpr);
1182298585Sjamie						if (trpr == tpr)
1183298585Sjamie							shm_prison_cleanup(tpr);
1184298585Sjamie					} else {
1185298585Sjamie						prison_unlock(tpr);
1186298585Sjamie						descend = 0;
1187298585Sjamie					}
1188298585Sjamie				}
1189298585Sjamie			}
1190298585Sjamie		}
1191298585Sjamie	}
1192298585Sjamie
1193298585Sjamie	return (0);
1194298585Sjamie}
1195298585Sjamie
1196298585Sjamiestatic int
1197298585Sjamieshm_prison_get(void *obj, void *data)
1198298585Sjamie{
1199298585Sjamie	struct prison *pr = obj;
1200298585Sjamie	struct prison *rpr;
1201298585Sjamie	struct vfsoptlist *opts = data;
1202298585Sjamie	int error, jsys;
1203298585Sjamie
1204298585Sjamie	/* Set sysvshm based on the jail's root prison. */
1205298585Sjamie	prison_lock(pr);
1206298585Sjamie	rpr = osd_jail_get(pr, shm_prison_slot);
1207298585Sjamie	prison_unlock(pr);
1208298585Sjamie	jsys = rpr == NULL ? JAIL_SYS_DISABLE
1209298585Sjamie	    : rpr == pr ? JAIL_SYS_NEW : JAIL_SYS_INHERIT;
1210298585Sjamie	error = vfs_setopt(opts, "sysvshm", &jsys, sizeof(jsys));
1211298585Sjamie	if (error == ENOENT)
1212298585Sjamie		error = 0;
1213298585Sjamie	return (error);
1214298585Sjamie}
1215298585Sjamie
1216298585Sjamiestatic int
1217298585Sjamieshm_prison_remove(void *obj, void *data __unused)
1218298585Sjamie{
1219298585Sjamie	struct prison *pr = obj;
1220298585Sjamie	struct prison *rpr;
1221298585Sjamie
1222298585Sjamie	SYSVSHM_LOCK();
1223298585Sjamie	prison_lock(pr);
1224298585Sjamie	rpr = osd_jail_get(pr, shm_prison_slot);
1225298585Sjamie	prison_unlock(pr);
1226298585Sjamie	if (rpr == pr)
1227298585Sjamie		shm_prison_cleanup(pr);
1228298585Sjamie	SYSVSHM_UNLOCK();
1229298585Sjamie	return (0);
1230298585Sjamie}
1231298585Sjamie
1232298585Sjamiestatic void
1233298585Sjamieshm_prison_cleanup(struct prison *pr)
1234298585Sjamie{
1235298585Sjamie	struct shmid_kernel *shmseg;
1236298585Sjamie	int i;
1237298585Sjamie
1238298585Sjamie	/* Remove any segments that belong to this jail. */
1239298585Sjamie	for (i = 0; i < shmalloced; i++) {
1240298585Sjamie		shmseg = &shmsegs[i];
1241298585Sjamie		if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) &&
1242298585Sjamie		    shmseg->cred != NULL && shmseg->cred->cr_prison == pr) {
1243298585Sjamie			shm_remove(shmseg, i);
1244298585Sjamie		}
1245298585Sjamie	}
1246298585Sjamie}
1247298585Sjamie
1248298585SjamieSYSCTL_JAIL_PARAM_SYS_NODE(sysvshm, CTLFLAG_RW, "SYSV shared memory");
1249298585Sjamie
1250194894Sjhb#if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
1251194894Sjhbstruct oshmid_ds {
1252194894Sjhb	struct	ipc_perm_old shm_perm;	/* operation perms */
1253194894Sjhb	int	shm_segsz;		/* size of segment (bytes) */
1254194894Sjhb	u_short	shm_cpid;		/* pid, creator */
1255194894Sjhb	u_short	shm_lpid;		/* pid, last operation */
1256194894Sjhb	short	shm_nattch;		/* no. of current attaches */
1257194894Sjhb	time_t	shm_atime;		/* last attach time */
1258194894Sjhb	time_t	shm_dtime;		/* last detach time */
1259194894Sjhb	time_t	shm_ctime;		/* last change time */
1260194894Sjhb	void	*shm_handle;		/* internal handle for shm segment */
1261194894Sjhb};
1262194894Sjhb
1263194894Sjhbstruct oshmctl_args {
1264194894Sjhb	int shmid;
1265194894Sjhb	int cmd;
1266194894Sjhb	struct oshmid_ds *ubuf;
1267194894Sjhb};
1268194894Sjhb
126977461Sddstatic int
1270194959Sjhboshmctl(struct thread *td, struct oshmctl_args *uap)
1271194894Sjhb{
1272194894Sjhb#ifdef COMPAT_43
1273194894Sjhb	int error = 0;
1274298585Sjamie	struct prison *rpr;
1275194894Sjhb	struct shmid_kernel *shmseg;
1276194894Sjhb	struct oshmid_ds outbuf;
1277194894Sjhb
1278298585Sjamie	rpr = shm_find_prison(td->td_ucred);
1279298585Sjamie	if (rpr == NULL)
1280194894Sjhb		return (ENOSYS);
1281282084Skib	if (uap->cmd != IPC_STAT) {
1282282084Skib		return (freebsd7_shmctl(td,
1283282084Skib		    (struct freebsd7_shmctl_args *)uap));
1284282084Skib	}
1285280323Skib	SYSVSHM_LOCK();
1286298585Sjamie	shmseg = shm_find_segment(rpr, uap->shmid, true);
1287194894Sjhb	if (shmseg == NULL) {
1288280323Skib		SYSVSHM_UNLOCK();
1289281094Skib		return (EINVAL);
1290194894Sjhb	}
1291282084Skib	error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
1292282084Skib	if (error != 0) {
1293282084Skib		SYSVSHM_UNLOCK();
1294282084Skib		return (error);
1295282084Skib	}
1296194894Sjhb#ifdef MAC
1297282084Skib	error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, uap->cmd);
1298282084Skib	if (error != 0) {
1299282084Skib		SYSVSHM_UNLOCK();
1300282084Skib		return (error);
1301282084Skib	}
1302194894Sjhb#endif
1303282084Skib	ipcperm_new2old(&shmseg->u.shm_perm, &outbuf.shm_perm);
1304282084Skib	outbuf.shm_segsz = shmseg->u.shm_segsz;
1305282084Skib	outbuf.shm_cpid = shmseg->u.shm_cpid;
1306282084Skib	outbuf.shm_lpid = shmseg->u.shm_lpid;
1307282084Skib	outbuf.shm_nattch = shmseg->u.shm_nattch;
1308282084Skib	outbuf.shm_atime = shmseg->u.shm_atime;
1309282084Skib	outbuf.shm_dtime = shmseg->u.shm_dtime;
1310282084Skib	outbuf.shm_ctime = shmseg->u.shm_ctime;
1311282084Skib	outbuf.shm_handle = shmseg->object;
1312280323Skib	SYSVSHM_UNLOCK();
1313285057Smjg	return (copyout(&outbuf, uap->ubuf, sizeof(outbuf)));
1314194894Sjhb#else
1315194894Sjhb	return (EINVAL);
1316194894Sjhb#endif
1317194894Sjhb}
1318194894Sjhb
1319194894Sjhb/* XXX casting to (sy_call_t *) is bogus, as usual. */
1320194894Sjhbstatic sy_call_t *shmcalls[] = {
1321225617Skmacy	(sy_call_t *)sys_shmat, (sy_call_t *)oshmctl,
1322225617Skmacy	(sy_call_t *)sys_shmdt, (sy_call_t *)sys_shmget,
1323194910Sjhb	(sy_call_t *)freebsd7_shmctl
1324194894Sjhb};
1325194894Sjhb
1326280323Skib#ifndef _SYS_SYSPROTO_H_
1327280323Skib/* XXX actually varargs. */
1328280323Skibstruct shmsys_args {
1329280323Skib	int	which;
1330280323Skib	int	a2;
1331280323Skib	int	a3;
1332280323Skib	int	a4;
1333280323Skib};
1334280323Skib#endif
1335194894Sjhbint
1336280323Skibsys_shmsys(struct thread *td, struct shmsys_args *uap)
1337194894Sjhb{
1338194894Sjhb
1339280323Skib	if (uap->which < 0 || uap->which >= nitems(shmcalls))
1340194894Sjhb		return (EINVAL);
1341285057Smjg	return ((*shmcalls[uap->which])(td, &uap->a2));
1342194894Sjhb}
1343194894Sjhb
1344194894Sjhb#endif	/* i386 && (COMPAT_FREEBSD4 || COMPAT_43) */
1345194894Sjhb
1346205323Skib#ifdef COMPAT_FREEBSD32
1347205323Skib
1348205323Skibint
1349205323Skibfreebsd32_shmsys(struct thread *td, struct freebsd32_shmsys_args *uap)
1350205323Skib{
1351205323Skib
1352194910Sjhb#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1353194910Sjhb    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1354205323Skib	switch (uap->which) {
1355205323Skib	case 0:	{	/* shmat */
1356205323Skib		struct shmat_args ap;
1357194910Sjhb
1358205323Skib		ap.shmid = uap->a2;
1359205323Skib		ap.shmaddr = PTRIN(uap->a3);
1360205323Skib		ap.shmflg = uap->a4;
1361205323Skib		return (sysent[SYS_shmat].sy_call(td, &ap));
1362205323Skib	}
1363205323Skib	case 2: {	/* shmdt */
1364205323Skib		struct shmdt_args ap;
1365205323Skib
1366205323Skib		ap.shmaddr = PTRIN(uap->a2);
1367205323Skib		return (sysent[SYS_shmdt].sy_call(td, &ap));
1368205323Skib	}
1369205323Skib	case 3: {	/* shmget */
1370205323Skib		struct shmget_args ap;
1371205323Skib
1372205323Skib		ap.key = uap->a2;
1373205323Skib		ap.size = uap->a3;
1374205323Skib		ap.shmflg = uap->a4;
1375205323Skib		return (sysent[SYS_shmget].sy_call(td, &ap));
1376205323Skib	}
1377205323Skib	case 4: {	/* shmctl */
1378205323Skib		struct freebsd7_freebsd32_shmctl_args ap;
1379205323Skib
1380205323Skib		ap.shmid = uap->a2;
1381205323Skib		ap.cmd = uap->a3;
1382205323Skib		ap.buf = PTRIN(uap->a4);
1383205323Skib		return (freebsd7_freebsd32_shmctl(td, &ap));
1384205323Skib	}
1385205323Skib	case 1:		/* oshmctl */
1386205323Skib	default:
1387205323Skib		return (EINVAL);
1388205323Skib	}
1389205323Skib#else
1390205323Skib	return (nosys(td, NULL));
1391205323Skib#endif
1392205323Skib}
1393205323Skib
1394205323Skib#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1395205323Skib    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1396205323Skibint
1397205323Skibfreebsd7_freebsd32_shmctl(struct thread *td,
1398205323Skib    struct freebsd7_freebsd32_shmctl_args *uap)
1399205323Skib{
1400285057Smjg	int error;
1401205323Skib	union {
1402205323Skib		struct shmid_ds shmid_ds;
1403205323Skib		struct shm_info shm_info;
1404205323Skib		struct shminfo shminfo;
1405205323Skib	} u;
1406205323Skib	union {
1407205323Skib		struct shmid_ds32_old shmid_ds32;
1408205323Skib		struct shm_info32 shm_info32;
1409205323Skib		struct shminfo32 shminfo32;
1410205323Skib	} u32;
1411205323Skib	size_t sz;
1412205323Skib
1413205323Skib	if (uap->cmd == IPC_SET) {
1414205323Skib		if ((error = copyin(uap->buf, &u32.shmid_ds32,
1415205323Skib		    sizeof(u32.shmid_ds32))))
1416205323Skib			goto done;
1417205323Skib		freebsd32_ipcperm_old_in(&u32.shmid_ds32.shm_perm,
1418205323Skib		    &u.shmid_ds.shm_perm);
1419205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_segsz);
1420205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_lpid);
1421205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_cpid);
1422205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_nattch);
1423205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_atime);
1424205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_dtime);
1425205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_ctime);
1426205323Skib	}
1427285057Smjg
1428205323Skib	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz);
1429205323Skib	if (error)
1430205323Skib		goto done;
1431285057Smjg
1432205323Skib	/* Cases in which we need to copyout */
1433205323Skib	switch (uap->cmd) {
1434205323Skib	case IPC_INFO:
1435205323Skib		CP(u.shminfo, u32.shminfo32, shmmax);
1436205323Skib		CP(u.shminfo, u32.shminfo32, shmmin);
1437205323Skib		CP(u.shminfo, u32.shminfo32, shmmni);
1438205323Skib		CP(u.shminfo, u32.shminfo32, shmseg);
1439205323Skib		CP(u.shminfo, u32.shminfo32, shmall);
1440205323Skib		error = copyout(&u32.shminfo32, uap->buf,
1441205323Skib		    sizeof(u32.shminfo32));
1442205323Skib		break;
1443205323Skib	case SHM_INFO:
1444205323Skib		CP(u.shm_info, u32.shm_info32, used_ids);
1445205323Skib		CP(u.shm_info, u32.shm_info32, shm_rss);
1446205323Skib		CP(u.shm_info, u32.shm_info32, shm_tot);
1447205323Skib		CP(u.shm_info, u32.shm_info32, shm_swp);
1448205323Skib		CP(u.shm_info, u32.shm_info32, swap_attempts);
1449205323Skib		CP(u.shm_info, u32.shm_info32, swap_successes);
1450205323Skib		error = copyout(&u32.shm_info32, uap->buf,
1451205323Skib		    sizeof(u32.shm_info32));
1452205323Skib		break;
1453205323Skib	case SHM_STAT:
1454205323Skib	case IPC_STAT:
1455331922Skib		memset(&u32.shmid_ds32, 0, sizeof(u32.shmid_ds32));
1456205323Skib		freebsd32_ipcperm_old_out(&u.shmid_ds.shm_perm,
1457205323Skib		    &u32.shmid_ds32.shm_perm);
1458205323Skib		if (u.shmid_ds.shm_segsz > INT32_MAX)
1459205323Skib			u32.shmid_ds32.shm_segsz = INT32_MAX;
1460205323Skib		else
1461205323Skib			CP(u.shmid_ds, u32.shmid_ds32, shm_segsz);
1462205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_lpid);
1463205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_cpid);
1464205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_nattch);
1465205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_atime);
1466205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_dtime);
1467205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_ctime);
1468205323Skib		u32.shmid_ds32.shm_internal = 0;
1469205323Skib		error = copyout(&u32.shmid_ds32, uap->buf,
1470205323Skib		    sizeof(u32.shmid_ds32));
1471205323Skib		break;
1472205323Skib	}
1473205323Skib
1474205323Skibdone:
1475205323Skib	if (error) {
1476205323Skib		/* Invalidate the return value */
1477205323Skib		td->td_retval[0] = -1;
1478205323Skib	}
1479205323Skib	return (error);
1480205323Skib}
1481205323Skib#endif
1482205323Skib
1483205323Skibint
1484205323Skibfreebsd32_shmctl(struct thread *td, struct freebsd32_shmctl_args *uap)
1485205323Skib{
1486285057Smjg	int error;
1487205323Skib	union {
1488205323Skib		struct shmid_ds shmid_ds;
1489205323Skib		struct shm_info shm_info;
1490205323Skib		struct shminfo shminfo;
1491205323Skib	} u;
1492205323Skib	union {
1493205323Skib		struct shmid_ds32 shmid_ds32;
1494205323Skib		struct shm_info32 shm_info32;
1495205323Skib		struct shminfo32 shminfo32;
1496205323Skib	} u32;
1497205323Skib	size_t sz;
1498285057Smjg
1499205323Skib	if (uap->cmd == IPC_SET) {
1500205323Skib		if ((error = copyin(uap->buf, &u32.shmid_ds32,
1501205323Skib		    sizeof(u32.shmid_ds32))))
1502205323Skib			goto done;
1503205323Skib		freebsd32_ipcperm_in(&u32.shmid_ds32.shm_perm,
1504205323Skib		    &u.shmid_ds.shm_perm);
1505205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_segsz);
1506205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_lpid);
1507205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_cpid);
1508205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_nattch);
1509205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_atime);
1510205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_dtime);
1511205323Skib		CP(u32.shmid_ds32, u.shmid_ds, shm_ctime);
1512205323Skib	}
1513285057Smjg
1514205323Skib	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz);
1515205323Skib	if (error)
1516205323Skib		goto done;
1517285057Smjg
1518205323Skib	/* Cases in which we need to copyout */
1519205323Skib	switch (uap->cmd) {
1520205323Skib	case IPC_INFO:
1521205323Skib		CP(u.shminfo, u32.shminfo32, shmmax);
1522205323Skib		CP(u.shminfo, u32.shminfo32, shmmin);
1523205323Skib		CP(u.shminfo, u32.shminfo32, shmmni);
1524205323Skib		CP(u.shminfo, u32.shminfo32, shmseg);
1525205323Skib		CP(u.shminfo, u32.shminfo32, shmall);
1526205323Skib		error = copyout(&u32.shminfo32, uap->buf,
1527205323Skib		    sizeof(u32.shminfo32));
1528205323Skib		break;
1529205323Skib	case SHM_INFO:
1530205323Skib		CP(u.shm_info, u32.shm_info32, used_ids);
1531205323Skib		CP(u.shm_info, u32.shm_info32, shm_rss);
1532205323Skib		CP(u.shm_info, u32.shm_info32, shm_tot);
1533205323Skib		CP(u.shm_info, u32.shm_info32, shm_swp);
1534205323Skib		CP(u.shm_info, u32.shm_info32, swap_attempts);
1535205323Skib		CP(u.shm_info, u32.shm_info32, swap_successes);
1536205323Skib		error = copyout(&u32.shm_info32, uap->buf,
1537205323Skib		    sizeof(u32.shm_info32));
1538205323Skib		break;
1539205323Skib	case SHM_STAT:
1540205323Skib	case IPC_STAT:
1541205323Skib		freebsd32_ipcperm_out(&u.shmid_ds.shm_perm,
1542205323Skib		    &u32.shmid_ds32.shm_perm);
1543205323Skib		if (u.shmid_ds.shm_segsz > INT32_MAX)
1544205323Skib			u32.shmid_ds32.shm_segsz = INT32_MAX;
1545205323Skib		else
1546205323Skib			CP(u.shmid_ds, u32.shmid_ds32, shm_segsz);
1547205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_lpid);
1548205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_cpid);
1549205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_nattch);
1550205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_atime);
1551205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_dtime);
1552205323Skib		CP(u.shmid_ds, u32.shmid_ds32, shm_ctime);
1553205323Skib		error = copyout(&u32.shmid_ds32, uap->buf,
1554205323Skib		    sizeof(u32.shmid_ds32));
1555205323Skib		break;
1556205323Skib	}
1557205323Skib
1558205323Skibdone:
1559205323Skib	if (error) {
1560205323Skib		/* Invalidate the return value */
1561205323Skib		td->td_retval[0] = -1;
1562205323Skib	}
1563205323Skib	return (error);
1564205323Skib}
1565205323Skib#endif
1566205323Skib
1567205323Skib#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1568205323Skib    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1569205323Skib
1570194910Sjhb#ifndef _SYS_SYSPROTO_H_
1571194910Sjhbstruct freebsd7_shmctl_args {
1572194910Sjhb	int shmid;
1573194910Sjhb	int cmd;
1574194910Sjhb	struct shmid_ds_old *buf;
1575194910Sjhb};
1576194910Sjhb#endif
1577194910Sjhbint
1578280323Skibfreebsd7_shmctl(struct thread *td, struct freebsd7_shmctl_args *uap)
1579194910Sjhb{
1580285057Smjg	int error;
1581194910Sjhb	struct shmid_ds_old old;
1582194910Sjhb	struct shmid_ds buf;
1583194910Sjhb	size_t bufsz;
1584285057Smjg
1585194910Sjhb	/*
1586194910Sjhb	 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
1587194910Sjhb	 * Linux binaries.  If we see the call come through the FreeBSD ABI,
1588194910Sjhb	 * return an error back to the user since we do not to support this.
1589194910Sjhb	 */
1590194910Sjhb	if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
1591194910Sjhb	    uap->cmd == SHM_STAT)
1592194910Sjhb		return (EINVAL);
1593194910Sjhb
1594194910Sjhb	/* IPC_SET needs to copyin the buffer before calling kern_shmctl */
1595194910Sjhb	if (uap->cmd == IPC_SET) {
1596194910Sjhb		if ((error = copyin(uap->buf, &old, sizeof(old))))
1597194910Sjhb			goto done;
1598194910Sjhb		ipcperm_old2new(&old.shm_perm, &buf.shm_perm);
1599194910Sjhb		CP(old, buf, shm_segsz);
1600194910Sjhb		CP(old, buf, shm_lpid);
1601194910Sjhb		CP(old, buf, shm_cpid);
1602194910Sjhb		CP(old, buf, shm_nattch);
1603194910Sjhb		CP(old, buf, shm_atime);
1604194910Sjhb		CP(old, buf, shm_dtime);
1605194910Sjhb		CP(old, buf, shm_ctime);
1606194910Sjhb	}
1607285057Smjg
1608194910Sjhb	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
1609194910Sjhb	if (error)
1610194910Sjhb		goto done;
1611194910Sjhb
1612194910Sjhb	/* Cases in which we need to copyout */
1613194910Sjhb	switch (uap->cmd) {
1614194910Sjhb	case IPC_STAT:
1615331922Skib		memset(&old, 0, sizeof(old));
1616194910Sjhb		ipcperm_new2old(&buf.shm_perm, &old.shm_perm);
1617194910Sjhb		if (buf.shm_segsz > INT_MAX)
1618194910Sjhb			old.shm_segsz = INT_MAX;
1619194910Sjhb		else
1620194910Sjhb			CP(buf, old, shm_segsz);
1621194910Sjhb		CP(buf, old, shm_lpid);
1622194910Sjhb		CP(buf, old, shm_cpid);
1623194910Sjhb		if (buf.shm_nattch > SHRT_MAX)
1624194910Sjhb			old.shm_nattch = SHRT_MAX;
1625194910Sjhb		else
1626194910Sjhb			CP(buf, old, shm_nattch);
1627194910Sjhb		CP(buf, old, shm_atime);
1628194910Sjhb		CP(buf, old, shm_dtime);
1629194910Sjhb		CP(buf, old, shm_ctime);
1630194910Sjhb		old.shm_internal = NULL;
1631194910Sjhb		error = copyout(&old, uap->buf, sizeof(old));
1632194910Sjhb		break;
1633194910Sjhb	}
1634194910Sjhb
1635194910Sjhbdone:
1636194910Sjhb	if (error) {
1637194910Sjhb		/* Invalidate the return value */
1638194910Sjhb		td->td_retval[0] = -1;
1639194910Sjhb	}
1640194910Sjhb	return (error);
1641194910Sjhb}
1642194910Sjhb
1643194910Sjhb#endif	/* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
1644194910Sjhb	   COMPAT_FREEBSD7 */
1645194910Sjhb
1646194894Sjhbstatic int
164769449Salfredsysvshm_modload(struct module *module, int cmd, void *arg)
164869449Salfred{
164969449Salfred	int error = 0;
165069449Salfred
165169449Salfred	switch (cmd) {
165269449Salfred	case MOD_LOAD:
1653205323Skib		error = shminit();
1654205323Skib		if (error != 0)
1655205323Skib			shmunload();
165669449Salfred		break;
165769449Salfred	case MOD_UNLOAD:
165869449Salfred		error = shmunload();
165969449Salfred		break;
166069449Salfred	case MOD_SHUTDOWN:
166169449Salfred		break;
166269449Salfred	default:
166369449Salfred		error = EINVAL;
166469449Salfred		break;
166569449Salfred	}
166669449Salfred	return (error);
166769449Salfred}
166869449Salfred
166971038Sdesstatic moduledata_t sysvshm_mod = {
167071038Sdes	"sysvshm",
167169449Salfred	&sysvshm_modload,
167269449Salfred	NULL
167369449Salfred};
167469449Salfred
1675194832SjhbDECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
167671038SdesMODULE_VERSION(sysvshm, 1);
1677