sysv_shm.c revision 220388
1/*	$NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $	*/
2/*-
3 * Copyright (c) 1994 Adam Glass and Charles Hannum.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Adam Glass and Charles
16 *	Hannum.
17 * 4. The names of the authors may not be used to endorse or promote products
18 *    derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31/*-
32 * Copyright (c) 2003-2005 McAfee, Inc.
33 * All rights reserved.
34 *
35 * This software was developed for the FreeBSD Project in part by McAfee
36 * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
37 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
38 * program.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 *    notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 *    notice, this list of conditions and the following disclaimer in the
47 *    documentation and/or other materials provided with the distribution.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 */
61
62#include <sys/cdefs.h>
63__FBSDID("$FreeBSD: head/sys/kern/sysv_shm.c 220388 2011-04-06 16:59:54Z trasz $");
64
65#include "opt_compat.h"
66#include "opt_sysvipc.h"
67
68#include <sys/param.h>
69#include <sys/systm.h>
70#include <sys/kernel.h>
71#include <sys/limits.h>
72#include <sys/lock.h>
73#include <sys/sysctl.h>
74#include <sys/shm.h>
75#include <sys/proc.h>
76#include <sys/malloc.h>
77#include <sys/mman.h>
78#include <sys/module.h>
79#include <sys/mutex.h>
80#include <sys/resourcevar.h>
81#include <sys/stat.h>
82#include <sys/syscall.h>
83#include <sys/syscallsubr.h>
84#include <sys/sysent.h>
85#include <sys/sysproto.h>
86#include <sys/jail.h>
87
88#include <security/mac/mac_framework.h>
89
90#include <vm/vm.h>
91#include <vm/vm_param.h>
92#include <vm/pmap.h>
93#include <vm/vm_object.h>
94#include <vm/vm_map.h>
95#include <vm/vm_page.h>
96#include <vm/vm_pager.h>
97
98FEATURE(sysv_shm, "System V shared memory segments support");
99
100static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
101
102static int shmget_allocate_segment(struct thread *td,
103    struct shmget_args *uap, int mode);
104static int shmget_existing(struct thread *td, struct shmget_args *uap,
105    int mode, int segnum);
106
107#define	SHMSEG_FREE     	0x0200
108#define	SHMSEG_REMOVED  	0x0400
109#define	SHMSEG_ALLOCATED	0x0800
110#define	SHMSEG_WANTED		0x1000
111
112static int shm_last_free, shm_nused, shmalloced;
113vm_size_t shm_committed;
114static struct shmid_kernel	*shmsegs;
115
116struct shmmap_state {
117	vm_offset_t va;
118	int shmid;
119};
120
121static void shm_deallocate_segment(struct shmid_kernel *);
122static int shm_find_segment_by_key(key_t);
123static struct shmid_kernel *shm_find_segment_by_shmid(int);
124static struct shmid_kernel *shm_find_segment_by_shmidx(int);
125static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *);
126static void shmrealloc(void);
127static int shminit(void);
128static int sysvshm_modload(struct module *, int, void *);
129static int shmunload(void);
130static void shmexit_myhook(struct vmspace *vm);
131static void shmfork_myhook(struct proc *p1, struct proc *p2);
132static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS);
133
134/*
135 * Tuneable values.
136 */
137#ifndef SHMMAXPGS
138#define	SHMMAXPGS	131072	/* Note: sysv shared memory is swap backed. */
139#endif
140#ifndef SHMMAX
141#define	SHMMAX	(SHMMAXPGS*PAGE_SIZE)
142#endif
143#ifndef SHMMIN
144#define	SHMMIN	1
145#endif
146#ifndef SHMMNI
147#define	SHMMNI	192
148#endif
149#ifndef SHMSEG
150#define	SHMSEG	128
151#endif
152#ifndef SHMALL
153#define	SHMALL	(SHMMAXPGS)
154#endif
155
156struct	shminfo shminfo = {
157	SHMMAX,
158	SHMMIN,
159	SHMMNI,
160	SHMSEG,
161	SHMALL
162};
163
164static int shm_use_phys;
165static int shm_allow_removed;
166
167SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0,
168    "Maximum shared memory segment size");
169SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0,
170    "Minimum shared memory segment size");
171SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0,
172    "Number of shared memory identifiers");
173SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0,
174    "Number of segments per process");
175SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0,
176    "Maximum number of pages available for shared memory");
177SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW,
178    &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core");
179SYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RW,
180    &shm_allow_removed, 0,
181    "Enable/Disable attachment to attached segments marked for removal");
182SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLTYPE_OPAQUE | CTLFLAG_RD,
183    NULL, 0, sysctl_shmsegs, "",
184    "Current number of shared memory segments allocated");
185
186static int
187shm_find_segment_by_key(key)
188	key_t key;
189{
190	int i;
191
192	for (i = 0; i < shmalloced; i++)
193		if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) &&
194		    shmsegs[i].u.shm_perm.key == key)
195			return (i);
196	return (-1);
197}
198
199static struct shmid_kernel *
200shm_find_segment_by_shmid(int shmid)
201{
202	int segnum;
203	struct shmid_kernel *shmseg;
204
205	segnum = IPCID_TO_IX(shmid);
206	if (segnum < 0 || segnum >= shmalloced)
207		return (NULL);
208	shmseg = &shmsegs[segnum];
209	if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
210	    (!shm_allow_removed &&
211	     (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0) ||
212	    shmseg->u.shm_perm.seq != IPCID_TO_SEQ(shmid))
213		return (NULL);
214	return (shmseg);
215}
216
217static struct shmid_kernel *
218shm_find_segment_by_shmidx(int segnum)
219{
220	struct shmid_kernel *shmseg;
221
222	if (segnum < 0 || segnum >= shmalloced)
223		return (NULL);
224	shmseg = &shmsegs[segnum];
225	if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
226	    (!shm_allow_removed &&
227	     (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0))
228		return (NULL);
229	return (shmseg);
230}
231
232static void
233shm_deallocate_segment(shmseg)
234	struct shmid_kernel *shmseg;
235{
236	vm_size_t size;
237
238	GIANT_REQUIRED;
239
240	vm_object_deallocate(shmseg->object);
241	shmseg->object = NULL;
242	size = round_page(shmseg->u.shm_segsz);
243	shm_committed -= btoc(size);
244	shm_nused--;
245	shmseg->u.shm_perm.mode = SHMSEG_FREE;
246#ifdef MAC
247	mac_sysvshm_cleanup(shmseg);
248#endif
249	crfree(shmseg->cred);
250	shmseg->cred = NULL;
251}
252
253static int
254shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s)
255{
256	struct shmid_kernel *shmseg;
257	int segnum, result;
258	vm_size_t size;
259
260	GIANT_REQUIRED;
261
262	segnum = IPCID_TO_IX(shmmap_s->shmid);
263	shmseg = &shmsegs[segnum];
264	size = round_page(shmseg->u.shm_segsz);
265	result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size);
266	if (result != KERN_SUCCESS)
267		return (EINVAL);
268	shmmap_s->shmid = -1;
269	shmseg->u.shm_dtime = time_second;
270	if ((--shmseg->u.shm_nattch <= 0) &&
271	    (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) {
272		shm_deallocate_segment(shmseg);
273		shm_last_free = segnum;
274	}
275	return (0);
276}
277
278#ifndef _SYS_SYSPROTO_H_
279struct shmdt_args {
280	const void *shmaddr;
281};
282#endif
283int
284shmdt(td, uap)
285	struct thread *td;
286	struct shmdt_args *uap;
287{
288	struct proc *p = td->td_proc;
289	struct shmmap_state *shmmap_s;
290#ifdef MAC
291	struct shmid_kernel *shmsegptr;
292#endif
293	int i;
294	int error = 0;
295
296	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
297		return (ENOSYS);
298	mtx_lock(&Giant);
299	shmmap_s = p->p_vmspace->vm_shm;
300 	if (shmmap_s == NULL) {
301		error = EINVAL;
302		goto done2;
303	}
304	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) {
305		if (shmmap_s->shmid != -1 &&
306		    shmmap_s->va == (vm_offset_t)uap->shmaddr) {
307			break;
308		}
309	}
310	if (i == shminfo.shmseg) {
311		error = EINVAL;
312		goto done2;
313	}
314#ifdef MAC
315	shmsegptr = &shmsegs[IPCID_TO_IX(shmmap_s->shmid)];
316	error = mac_sysvshm_check_shmdt(td->td_ucred, shmsegptr);
317	if (error != 0)
318		goto done2;
319#endif
320	error = shm_delete_mapping(p->p_vmspace, shmmap_s);
321done2:
322	mtx_unlock(&Giant);
323	return (error);
324}
325
326#ifndef _SYS_SYSPROTO_H_
327struct shmat_args {
328	int shmid;
329	const void *shmaddr;
330	int shmflg;
331};
332#endif
333int
334kern_shmat(td, shmid, shmaddr, shmflg)
335	struct thread *td;
336	int shmid;
337	const void *shmaddr;
338	int shmflg;
339{
340	struct proc *p = td->td_proc;
341	int i, flags;
342	struct shmid_kernel *shmseg;
343	struct shmmap_state *shmmap_s = NULL;
344	vm_offset_t attach_va;
345	vm_prot_t prot;
346	vm_size_t size;
347	int rv;
348	int error = 0;
349
350	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
351		return (ENOSYS);
352	mtx_lock(&Giant);
353	shmmap_s = p->p_vmspace->vm_shm;
354	if (shmmap_s == NULL) {
355		shmmap_s = malloc(shminfo.shmseg * sizeof(struct shmmap_state),
356		    M_SHM, M_WAITOK);
357		for (i = 0; i < shminfo.shmseg; i++)
358			shmmap_s[i].shmid = -1;
359		p->p_vmspace->vm_shm = shmmap_s;
360	}
361	shmseg = shm_find_segment_by_shmid(shmid);
362	if (shmseg == NULL) {
363		error = EINVAL;
364		goto done2;
365	}
366	error = ipcperm(td, &shmseg->u.shm_perm,
367	    (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
368	if (error)
369		goto done2;
370#ifdef MAC
371	error = mac_sysvshm_check_shmat(td->td_ucred, shmseg, shmflg);
372	if (error != 0)
373		goto done2;
374#endif
375	for (i = 0; i < shminfo.shmseg; i++) {
376		if (shmmap_s->shmid == -1)
377			break;
378		shmmap_s++;
379	}
380	if (i >= shminfo.shmseg) {
381		error = EMFILE;
382		goto done2;
383	}
384	size = round_page(shmseg->u.shm_segsz);
385	prot = VM_PROT_READ;
386	if ((shmflg & SHM_RDONLY) == 0)
387		prot |= VM_PROT_WRITE;
388	flags = MAP_ANON | MAP_SHARED;
389	if (shmaddr) {
390		flags |= MAP_FIXED;
391		if (shmflg & SHM_RND) {
392			attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1);
393		} else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) {
394			attach_va = (vm_offset_t)shmaddr;
395		} else {
396			error = EINVAL;
397			goto done2;
398		}
399	} else {
400		/*
401		 * This is just a hint to vm_map_find() about where to
402		 * put it.
403		 */
404		PROC_LOCK(p);
405		attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr +
406		    lim_max(p, RLIMIT_DATA));
407		PROC_UNLOCK(p);
408	}
409
410	vm_object_reference(shmseg->object);
411	rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object,
412	    0, &attach_va, size, (flags & MAP_FIXED) ? VMFS_NO_SPACE :
413	    VMFS_ANY_SPACE, prot, prot, 0);
414	if (rv != KERN_SUCCESS) {
415		vm_object_deallocate(shmseg->object);
416		error = ENOMEM;
417		goto done2;
418	}
419	vm_map_inherit(&p->p_vmspace->vm_map,
420		attach_va, attach_va + size, VM_INHERIT_SHARE);
421
422	shmmap_s->va = attach_va;
423	shmmap_s->shmid = shmid;
424	shmseg->u.shm_lpid = p->p_pid;
425	shmseg->u.shm_atime = time_second;
426	shmseg->u.shm_nattch++;
427	td->td_retval[0] = attach_va;
428done2:
429	mtx_unlock(&Giant);
430	return (error);
431}
432
433int
434shmat(td, uap)
435	struct thread *td;
436	struct shmat_args *uap;
437{
438	return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg);
439}
440
441int
442kern_shmctl(td, shmid, cmd, buf, bufsz)
443	struct thread *td;
444	int shmid;
445	int cmd;
446	void *buf;
447	size_t *bufsz;
448{
449	int error = 0;
450	struct shmid_kernel *shmseg;
451
452	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
453		return (ENOSYS);
454
455	mtx_lock(&Giant);
456	switch (cmd) {
457	/*
458	 * It is possible that kern_shmctl is being called from the Linux ABI
459	 * layer, in which case, we will need to implement IPC_INFO.  It should
460	 * be noted that other shmctl calls will be funneled through here for
461	 * Linix binaries as well.
462	 *
463	 * NB: The Linux ABI layer will convert this data to structure(s) more
464	 * consistent with the Linux ABI.
465	 */
466	case IPC_INFO:
467		memcpy(buf, &shminfo, sizeof(shminfo));
468		if (bufsz)
469			*bufsz = sizeof(shminfo);
470		td->td_retval[0] = shmalloced;
471		goto done2;
472	case SHM_INFO: {
473		struct shm_info shm_info;
474		shm_info.used_ids = shm_nused;
475		shm_info.shm_rss = 0;	/*XXX where to get from ? */
476		shm_info.shm_tot = 0;	/*XXX where to get from ? */
477		shm_info.shm_swp = 0;	/*XXX where to get from ? */
478		shm_info.swap_attempts = 0;	/*XXX where to get from ? */
479		shm_info.swap_successes = 0;	/*XXX where to get from ? */
480		memcpy(buf, &shm_info, sizeof(shm_info));
481		if (bufsz)
482			*bufsz = sizeof(shm_info);
483		td->td_retval[0] = shmalloced;
484		goto done2;
485	}
486	}
487	if (cmd == SHM_STAT)
488		shmseg = shm_find_segment_by_shmidx(shmid);
489	else
490		shmseg = shm_find_segment_by_shmid(shmid);
491	if (shmseg == NULL) {
492		error = EINVAL;
493		goto done2;
494	}
495#ifdef MAC
496	error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd);
497	if (error != 0)
498		goto done2;
499#endif
500	switch (cmd) {
501	case SHM_STAT:
502	case IPC_STAT:
503		error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
504		if (error)
505			goto done2;
506		memcpy(buf, &shmseg->u, sizeof(struct shmid_ds));
507		if (bufsz)
508			*bufsz = sizeof(struct shmid_ds);
509		if (cmd == SHM_STAT)
510			td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->u.shm_perm);
511		break;
512	case IPC_SET: {
513		struct shmid_ds *shmid;
514
515		shmid = (struct shmid_ds *)buf;
516		error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
517		if (error)
518			goto done2;
519		shmseg->u.shm_perm.uid = shmid->shm_perm.uid;
520		shmseg->u.shm_perm.gid = shmid->shm_perm.gid;
521		shmseg->u.shm_perm.mode =
522		    (shmseg->u.shm_perm.mode & ~ACCESSPERMS) |
523		    (shmid->shm_perm.mode & ACCESSPERMS);
524		shmseg->u.shm_ctime = time_second;
525		break;
526	}
527	case IPC_RMID:
528		error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
529		if (error)
530			goto done2;
531		shmseg->u.shm_perm.key = IPC_PRIVATE;
532		shmseg->u.shm_perm.mode |= SHMSEG_REMOVED;
533		if (shmseg->u.shm_nattch <= 0) {
534			shm_deallocate_segment(shmseg);
535			shm_last_free = IPCID_TO_IX(shmid);
536		}
537		break;
538#if 0
539	case SHM_LOCK:
540	case SHM_UNLOCK:
541#endif
542	default:
543		error = EINVAL;
544		break;
545	}
546done2:
547	mtx_unlock(&Giant);
548	return (error);
549}
550
551#ifndef _SYS_SYSPROTO_H_
552struct shmctl_args {
553	int shmid;
554	int cmd;
555	struct shmid_ds *buf;
556};
557#endif
558int
559shmctl(td, uap)
560	struct thread *td;
561	struct shmctl_args *uap;
562{
563	int error = 0;
564	struct shmid_ds buf;
565	size_t bufsz;
566
567	/*
568	 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
569	 * Linux binaries.  If we see the call come through the FreeBSD ABI,
570	 * return an error back to the user since we do not to support this.
571	 */
572	if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
573	    uap->cmd == SHM_STAT)
574		return (EINVAL);
575
576	/* IPC_SET needs to copyin the buffer before calling kern_shmctl */
577	if (uap->cmd == IPC_SET) {
578		if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds))))
579			goto done;
580	}
581
582	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
583	if (error)
584		goto done;
585
586	/* Cases in which we need to copyout */
587	switch (uap->cmd) {
588	case IPC_STAT:
589		error = copyout(&buf, uap->buf, bufsz);
590		break;
591	}
592
593done:
594	if (error) {
595		/* Invalidate the return value */
596		td->td_retval[0] = -1;
597	}
598	return (error);
599}
600
601
602static int
603shmget_existing(td, uap, mode, segnum)
604	struct thread *td;
605	struct shmget_args *uap;
606	int mode;
607	int segnum;
608{
609	struct shmid_kernel *shmseg;
610	int error;
611
612	shmseg = &shmsegs[segnum];
613	if (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) {
614		/*
615		 * This segment is in the process of being allocated.  Wait
616		 * until it's done, and look the key up again (in case the
617		 * allocation failed or it was freed).
618		 */
619		shmseg->u.shm_perm.mode |= SHMSEG_WANTED;
620		error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0);
621		if (error)
622			return (error);
623		return (EAGAIN);
624	}
625	if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
626		return (EEXIST);
627#ifdef MAC
628	error = mac_sysvshm_check_shmget(td->td_ucred, shmseg, uap->shmflg);
629	if (error != 0)
630		return (error);
631#endif
632	if (uap->size != 0 && uap->size > shmseg->u.shm_segsz)
633		return (EINVAL);
634	td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
635	return (0);
636}
637
638static int
639shmget_allocate_segment(td, uap, mode)
640	struct thread *td;
641	struct shmget_args *uap;
642	int mode;
643{
644	int i, segnum, shmid;
645	size_t size;
646	struct ucred *cred = td->td_ucred;
647	struct shmid_kernel *shmseg;
648	vm_object_t shm_object;
649
650	GIANT_REQUIRED;
651
652	if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
653		return (EINVAL);
654	if (shm_nused >= shminfo.shmmni) /* Any shmids left? */
655		return (ENOSPC);
656	size = round_page(uap->size);
657	if (shm_committed + btoc(size) > shminfo.shmall)
658		return (ENOMEM);
659	if (shm_last_free < 0) {
660		shmrealloc();	/* Maybe expand the shmsegs[] array. */
661		for (i = 0; i < shmalloced; i++)
662			if (shmsegs[i].u.shm_perm.mode & SHMSEG_FREE)
663				break;
664		if (i == shmalloced)
665			return (ENOSPC);
666		segnum = i;
667	} else  {
668		segnum = shm_last_free;
669		shm_last_free = -1;
670	}
671	shmseg = &shmsegs[segnum];
672	/*
673	 * In case we sleep in malloc(), mark the segment present but deleted
674	 * so that noone else tries to create the same key.
675	 */
676	shmseg->u.shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
677	shmseg->u.shm_perm.key = uap->key;
678	shmseg->u.shm_perm.seq = (shmseg->u.shm_perm.seq + 1) & 0x7fff;
679	shmid = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
680
681	/*
682	 * We make sure that we have allocated a pager before we need
683	 * to.
684	 */
685	shm_object = vm_pager_allocate(shm_use_phys ? OBJT_PHYS : OBJT_SWAP,
686	    0, size, VM_PROT_DEFAULT, 0, cred);
687	if (shm_object == NULL)
688		return (ENOMEM);
689	VM_OBJECT_LOCK(shm_object);
690	vm_object_clear_flag(shm_object, OBJ_ONEMAPPING);
691	vm_object_set_flag(shm_object, OBJ_NOSPLIT);
692	VM_OBJECT_UNLOCK(shm_object);
693
694	shmseg->object = shm_object;
695	shmseg->u.shm_perm.cuid = shmseg->u.shm_perm.uid = cred->cr_uid;
696	shmseg->u.shm_perm.cgid = shmseg->u.shm_perm.gid = cred->cr_gid;
697	shmseg->u.shm_perm.mode = (shmseg->u.shm_perm.mode & SHMSEG_WANTED) |
698	    (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
699	crhold(cred);
700	shmseg->cred = cred;
701	shmseg->u.shm_segsz = uap->size;
702	shmseg->u.shm_cpid = td->td_proc->p_pid;
703	shmseg->u.shm_lpid = shmseg->u.shm_nattch = 0;
704	shmseg->u.shm_atime = shmseg->u.shm_dtime = 0;
705#ifdef MAC
706	mac_sysvshm_create(cred, shmseg);
707#endif
708	shmseg->u.shm_ctime = time_second;
709	shm_committed += btoc(size);
710	shm_nused++;
711	if (shmseg->u.shm_perm.mode & SHMSEG_WANTED) {
712		/*
713		 * Somebody else wanted this key while we were asleep.  Wake
714		 * them up now.
715		 */
716		shmseg->u.shm_perm.mode &= ~SHMSEG_WANTED;
717		wakeup(shmseg);
718	}
719	td->td_retval[0] = shmid;
720	return (0);
721}
722
723#ifndef _SYS_SYSPROTO_H_
724struct shmget_args {
725	key_t key;
726	size_t size;
727	int shmflg;
728};
729#endif
730int
731shmget(td, uap)
732	struct thread *td;
733	struct shmget_args *uap;
734{
735	int segnum, mode;
736	int error;
737
738	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
739		return (ENOSYS);
740	mtx_lock(&Giant);
741	mode = uap->shmflg & ACCESSPERMS;
742	if (uap->key != IPC_PRIVATE) {
743	again:
744		segnum = shm_find_segment_by_key(uap->key);
745		if (segnum >= 0) {
746			error = shmget_existing(td, uap, mode, segnum);
747			if (error == EAGAIN)
748				goto again;
749			goto done2;
750		}
751		if ((uap->shmflg & IPC_CREAT) == 0) {
752			error = ENOENT;
753			goto done2;
754		}
755	}
756	error = shmget_allocate_segment(td, uap, mode);
757done2:
758	mtx_unlock(&Giant);
759	return (error);
760}
761
762static void
763shmfork_myhook(p1, p2)
764	struct proc *p1, *p2;
765{
766	struct shmmap_state *shmmap_s;
767	size_t size;
768	int i;
769
770	mtx_lock(&Giant);
771	size = shminfo.shmseg * sizeof(struct shmmap_state);
772	shmmap_s = malloc(size, M_SHM, M_WAITOK);
773	bcopy(p1->p_vmspace->vm_shm, shmmap_s, size);
774	p2->p_vmspace->vm_shm = shmmap_s;
775	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
776		if (shmmap_s->shmid != -1)
777			shmsegs[IPCID_TO_IX(shmmap_s->shmid)].u.shm_nattch++;
778	mtx_unlock(&Giant);
779}
780
781static void
782shmexit_myhook(struct vmspace *vm)
783{
784	struct shmmap_state *base, *shm;
785	int i;
786
787	if ((base = vm->vm_shm) != NULL) {
788		vm->vm_shm = NULL;
789		mtx_lock(&Giant);
790		for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) {
791			if (shm->shmid != -1)
792				shm_delete_mapping(vm, shm);
793		}
794		mtx_unlock(&Giant);
795		free(base, M_SHM);
796	}
797}
798
799static void
800shmrealloc(void)
801{
802	int i;
803	struct shmid_kernel *newsegs;
804
805	if (shmalloced >= shminfo.shmmni)
806		return;
807
808	newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK);
809	if (newsegs == NULL)
810		return;
811	for (i = 0; i < shmalloced; i++)
812		bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
813	for (; i < shminfo.shmmni; i++) {
814		shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
815		shmsegs[i].u.shm_perm.seq = 0;
816#ifdef MAC
817		mac_sysvshm_init(&shmsegs[i]);
818#endif
819	}
820	free(shmsegs, M_SHM);
821	shmsegs = newsegs;
822	shmalloced = shminfo.shmmni;
823}
824
825static struct syscall_helper_data shm_syscalls[] = {
826	SYSCALL_INIT_HELPER(shmat),
827	SYSCALL_INIT_HELPER(shmctl),
828	SYSCALL_INIT_HELPER(shmdt),
829	SYSCALL_INIT_HELPER(shmget),
830#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
831    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
832	SYSCALL_INIT_HELPER(freebsd7_shmctl),
833#endif
834#if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
835	SYSCALL_INIT_HELPER(shmsys),
836#endif
837	SYSCALL_INIT_LAST
838};
839
840#ifdef COMPAT_FREEBSD32
841#include <compat/freebsd32/freebsd32.h>
842#include <compat/freebsd32/freebsd32_ipc.h>
843#include <compat/freebsd32/freebsd32_proto.h>
844#include <compat/freebsd32/freebsd32_signal.h>
845#include <compat/freebsd32/freebsd32_syscall.h>
846#include <compat/freebsd32/freebsd32_util.h>
847
848static struct syscall_helper_data shm32_syscalls[] = {
849	SYSCALL32_INIT_HELPER(shmat),
850	SYSCALL32_INIT_HELPER(shmdt),
851	SYSCALL32_INIT_HELPER(shmget),
852	SYSCALL32_INIT_HELPER(freebsd32_shmsys),
853	SYSCALL32_INIT_HELPER(freebsd32_shmctl),
854#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
855    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
856	SYSCALL32_INIT_HELPER(freebsd7_freebsd32_shmctl),
857#endif
858	SYSCALL_INIT_LAST
859};
860#endif
861
862static int
863shminit()
864{
865	int i, error;
866
867#ifndef BURN_BRIDGES
868	if (TUNABLE_ULONG_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall) != 0)
869		printf("kern.ipc.shmmaxpgs is now called kern.ipc.shmall!\n");
870#endif
871	TUNABLE_ULONG_FETCH("kern.ipc.shmall", &shminfo.shmall);
872
873	/* Initialize shmmax dealing with possible overflow. */
874	for (i = PAGE_SIZE; i > 0; i--) {
875		shminfo.shmmax = shminfo.shmall * i;
876		if (shminfo.shmmax >= shminfo.shmall)
877			break;
878	}
879
880	TUNABLE_ULONG_FETCH("kern.ipc.shmmin", &shminfo.shmmin);
881	TUNABLE_ULONG_FETCH("kern.ipc.shmmni", &shminfo.shmmni);
882	TUNABLE_ULONG_FETCH("kern.ipc.shmseg", &shminfo.shmseg);
883	TUNABLE_INT_FETCH("kern.ipc.shm_use_phys", &shm_use_phys);
884
885	shmalloced = shminfo.shmmni;
886	shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK);
887	for (i = 0; i < shmalloced; i++) {
888		shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
889		shmsegs[i].u.shm_perm.seq = 0;
890#ifdef MAC
891		mac_sysvshm_init(&shmsegs[i]);
892#endif
893	}
894	shm_last_free = 0;
895	shm_nused = 0;
896	shm_committed = 0;
897	shmexit_hook = &shmexit_myhook;
898	shmfork_hook = &shmfork_myhook;
899
900	error = syscall_helper_register(shm_syscalls);
901	if (error != 0)
902		return (error);
903#ifdef COMPAT_FREEBSD32
904	error = syscall32_helper_register(shm32_syscalls);
905	if (error != 0)
906		return (error);
907#endif
908	return (0);
909}
910
911static int
912shmunload()
913{
914	int i;
915
916	if (shm_nused > 0)
917		return (EBUSY);
918
919#ifdef COMPAT_FREEBSD32
920	syscall32_helper_unregister(shm32_syscalls);
921#endif
922	syscall_helper_unregister(shm_syscalls);
923
924	for (i = 0; i < shmalloced; i++) {
925#ifdef MAC
926		mac_sysvshm_destroy(&shmsegs[i]);
927#endif
928		/*
929		 * Objects might be still mapped into the processes
930		 * address spaces.  Actual free would happen on the
931		 * last mapping destruction.
932		 */
933		if (shmsegs[i].u.shm_perm.mode != SHMSEG_FREE)
934			vm_object_deallocate(shmsegs[i].object);
935	}
936	free(shmsegs, M_SHM);
937	shmexit_hook = NULL;
938	shmfork_hook = NULL;
939	return (0);
940}
941
942static int
943sysctl_shmsegs(SYSCTL_HANDLER_ARGS)
944{
945
946	return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0])));
947}
948
949#if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
950struct oshmid_ds {
951	struct	ipc_perm_old shm_perm;	/* operation perms */
952	int	shm_segsz;		/* size of segment (bytes) */
953	u_short	shm_cpid;		/* pid, creator */
954	u_short	shm_lpid;		/* pid, last operation */
955	short	shm_nattch;		/* no. of current attaches */
956	time_t	shm_atime;		/* last attach time */
957	time_t	shm_dtime;		/* last detach time */
958	time_t	shm_ctime;		/* last change time */
959	void	*shm_handle;		/* internal handle for shm segment */
960};
961
962struct oshmctl_args {
963	int shmid;
964	int cmd;
965	struct oshmid_ds *ubuf;
966};
967
968static int
969oshmctl(struct thread *td, struct oshmctl_args *uap)
970{
971#ifdef COMPAT_43
972	int error = 0;
973	struct shmid_kernel *shmseg;
974	struct oshmid_ds outbuf;
975
976	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
977		return (ENOSYS);
978	mtx_lock(&Giant);
979	shmseg = shm_find_segment_by_shmid(uap->shmid);
980	if (shmseg == NULL) {
981		error = EINVAL;
982		goto done2;
983	}
984	switch (uap->cmd) {
985	case IPC_STAT:
986		error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
987		if (error)
988			goto done2;
989#ifdef MAC
990		error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, uap->cmd);
991		if (error != 0)
992			goto done2;
993#endif
994		ipcperm_new2old(&shmseg->u.shm_perm, &outbuf.shm_perm);
995		outbuf.shm_segsz = shmseg->u.shm_segsz;
996		outbuf.shm_cpid = shmseg->u.shm_cpid;
997		outbuf.shm_lpid = shmseg->u.shm_lpid;
998		outbuf.shm_nattch = shmseg->u.shm_nattch;
999		outbuf.shm_atime = shmseg->u.shm_atime;
1000		outbuf.shm_dtime = shmseg->u.shm_dtime;
1001		outbuf.shm_ctime = shmseg->u.shm_ctime;
1002		outbuf.shm_handle = shmseg->object;
1003		error = copyout(&outbuf, uap->ubuf, sizeof(outbuf));
1004		if (error)
1005			goto done2;
1006		break;
1007	default:
1008		error = freebsd7_shmctl(td, (struct freebsd7_shmctl_args *)uap);
1009		break;
1010	}
1011done2:
1012	mtx_unlock(&Giant);
1013	return (error);
1014#else
1015	return (EINVAL);
1016#endif
1017}
1018
1019/* XXX casting to (sy_call_t *) is bogus, as usual. */
1020static sy_call_t *shmcalls[] = {
1021	(sy_call_t *)shmat, (sy_call_t *)oshmctl,
1022	(sy_call_t *)shmdt, (sy_call_t *)shmget,
1023	(sy_call_t *)freebsd7_shmctl
1024};
1025
1026int
1027shmsys(td, uap)
1028	struct thread *td;
1029	/* XXX actually varargs. */
1030	struct shmsys_args /* {
1031		int	which;
1032		int	a2;
1033		int	a3;
1034		int	a4;
1035	} */ *uap;
1036{
1037	int error;
1038
1039	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
1040		return (ENOSYS);
1041	if (uap->which < 0 ||
1042	    uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
1043		return (EINVAL);
1044	mtx_lock(&Giant);
1045	error = (*shmcalls[uap->which])(td, &uap->a2);
1046	mtx_unlock(&Giant);
1047	return (error);
1048}
1049
1050#endif	/* i386 && (COMPAT_FREEBSD4 || COMPAT_43) */
1051
1052#ifdef COMPAT_FREEBSD32
1053
1054int
1055freebsd32_shmsys(struct thread *td, struct freebsd32_shmsys_args *uap)
1056{
1057
1058#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1059    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1060	switch (uap->which) {
1061	case 0:	{	/* shmat */
1062		struct shmat_args ap;
1063
1064		ap.shmid = uap->a2;
1065		ap.shmaddr = PTRIN(uap->a3);
1066		ap.shmflg = uap->a4;
1067		return (sysent[SYS_shmat].sy_call(td, &ap));
1068	}
1069	case 2: {	/* shmdt */
1070		struct shmdt_args ap;
1071
1072		ap.shmaddr = PTRIN(uap->a2);
1073		return (sysent[SYS_shmdt].sy_call(td, &ap));
1074	}
1075	case 3: {	/* shmget */
1076		struct shmget_args ap;
1077
1078		ap.key = uap->a2;
1079		ap.size = uap->a3;
1080		ap.shmflg = uap->a4;
1081		return (sysent[SYS_shmget].sy_call(td, &ap));
1082	}
1083	case 4: {	/* shmctl */
1084		struct freebsd7_freebsd32_shmctl_args ap;
1085
1086		ap.shmid = uap->a2;
1087		ap.cmd = uap->a3;
1088		ap.buf = PTRIN(uap->a4);
1089		return (freebsd7_freebsd32_shmctl(td, &ap));
1090	}
1091	case 1:		/* oshmctl */
1092	default:
1093		return (EINVAL);
1094	}
1095#else
1096	return (nosys(td, NULL));
1097#endif
1098}
1099
1100#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1101    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1102int
1103freebsd7_freebsd32_shmctl(struct thread *td,
1104    struct freebsd7_freebsd32_shmctl_args *uap)
1105{
1106	int error = 0;
1107	union {
1108		struct shmid_ds shmid_ds;
1109		struct shm_info shm_info;
1110		struct shminfo shminfo;
1111	} u;
1112	union {
1113		struct shmid_ds32_old shmid_ds32;
1114		struct shm_info32 shm_info32;
1115		struct shminfo32 shminfo32;
1116	} u32;
1117	size_t sz;
1118
1119	if (uap->cmd == IPC_SET) {
1120		if ((error = copyin(uap->buf, &u32.shmid_ds32,
1121		    sizeof(u32.shmid_ds32))))
1122			goto done;
1123		freebsd32_ipcperm_old_in(&u32.shmid_ds32.shm_perm,
1124		    &u.shmid_ds.shm_perm);
1125		CP(u32.shmid_ds32, u.shmid_ds, shm_segsz);
1126		CP(u32.shmid_ds32, u.shmid_ds, shm_lpid);
1127		CP(u32.shmid_ds32, u.shmid_ds, shm_cpid);
1128		CP(u32.shmid_ds32, u.shmid_ds, shm_nattch);
1129		CP(u32.shmid_ds32, u.shmid_ds, shm_atime);
1130		CP(u32.shmid_ds32, u.shmid_ds, shm_dtime);
1131		CP(u32.shmid_ds32, u.shmid_ds, shm_ctime);
1132	}
1133
1134	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz);
1135	if (error)
1136		goto done;
1137
1138	/* Cases in which we need to copyout */
1139	switch (uap->cmd) {
1140	case IPC_INFO:
1141		CP(u.shminfo, u32.shminfo32, shmmax);
1142		CP(u.shminfo, u32.shminfo32, shmmin);
1143		CP(u.shminfo, u32.shminfo32, shmmni);
1144		CP(u.shminfo, u32.shminfo32, shmseg);
1145		CP(u.shminfo, u32.shminfo32, shmall);
1146		error = copyout(&u32.shminfo32, uap->buf,
1147		    sizeof(u32.shminfo32));
1148		break;
1149	case SHM_INFO:
1150		CP(u.shm_info, u32.shm_info32, used_ids);
1151		CP(u.shm_info, u32.shm_info32, shm_rss);
1152		CP(u.shm_info, u32.shm_info32, shm_tot);
1153		CP(u.shm_info, u32.shm_info32, shm_swp);
1154		CP(u.shm_info, u32.shm_info32, swap_attempts);
1155		CP(u.shm_info, u32.shm_info32, swap_successes);
1156		error = copyout(&u32.shm_info32, uap->buf,
1157		    sizeof(u32.shm_info32));
1158		break;
1159	case SHM_STAT:
1160	case IPC_STAT:
1161		freebsd32_ipcperm_old_out(&u.shmid_ds.shm_perm,
1162		    &u32.shmid_ds32.shm_perm);
1163		if (u.shmid_ds.shm_segsz > INT32_MAX)
1164			u32.shmid_ds32.shm_segsz = INT32_MAX;
1165		else
1166			CP(u.shmid_ds, u32.shmid_ds32, shm_segsz);
1167		CP(u.shmid_ds, u32.shmid_ds32, shm_lpid);
1168		CP(u.shmid_ds, u32.shmid_ds32, shm_cpid);
1169		CP(u.shmid_ds, u32.shmid_ds32, shm_nattch);
1170		CP(u.shmid_ds, u32.shmid_ds32, shm_atime);
1171		CP(u.shmid_ds, u32.shmid_ds32, shm_dtime);
1172		CP(u.shmid_ds, u32.shmid_ds32, shm_ctime);
1173		u32.shmid_ds32.shm_internal = 0;
1174		error = copyout(&u32.shmid_ds32, uap->buf,
1175		    sizeof(u32.shmid_ds32));
1176		break;
1177	}
1178
1179done:
1180	if (error) {
1181		/* Invalidate the return value */
1182		td->td_retval[0] = -1;
1183	}
1184	return (error);
1185}
1186#endif
1187
1188int
1189freebsd32_shmctl(struct thread *td, struct freebsd32_shmctl_args *uap)
1190{
1191	int error = 0;
1192	union {
1193		struct shmid_ds shmid_ds;
1194		struct shm_info shm_info;
1195		struct shminfo shminfo;
1196	} u;
1197	union {
1198		struct shmid_ds32 shmid_ds32;
1199		struct shm_info32 shm_info32;
1200		struct shminfo32 shminfo32;
1201	} u32;
1202	size_t sz;
1203
1204	if (uap->cmd == IPC_SET) {
1205		if ((error = copyin(uap->buf, &u32.shmid_ds32,
1206		    sizeof(u32.shmid_ds32))))
1207			goto done;
1208		freebsd32_ipcperm_in(&u32.shmid_ds32.shm_perm,
1209		    &u.shmid_ds.shm_perm);
1210		CP(u32.shmid_ds32, u.shmid_ds, shm_segsz);
1211		CP(u32.shmid_ds32, u.shmid_ds, shm_lpid);
1212		CP(u32.shmid_ds32, u.shmid_ds, shm_cpid);
1213		CP(u32.shmid_ds32, u.shmid_ds, shm_nattch);
1214		CP(u32.shmid_ds32, u.shmid_ds, shm_atime);
1215		CP(u32.shmid_ds32, u.shmid_ds, shm_dtime);
1216		CP(u32.shmid_ds32, u.shmid_ds, shm_ctime);
1217	}
1218
1219	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz);
1220	if (error)
1221		goto done;
1222
1223	/* Cases in which we need to copyout */
1224	switch (uap->cmd) {
1225	case IPC_INFO:
1226		CP(u.shminfo, u32.shminfo32, shmmax);
1227		CP(u.shminfo, u32.shminfo32, shmmin);
1228		CP(u.shminfo, u32.shminfo32, shmmni);
1229		CP(u.shminfo, u32.shminfo32, shmseg);
1230		CP(u.shminfo, u32.shminfo32, shmall);
1231		error = copyout(&u32.shminfo32, uap->buf,
1232		    sizeof(u32.shminfo32));
1233		break;
1234	case SHM_INFO:
1235		CP(u.shm_info, u32.shm_info32, used_ids);
1236		CP(u.shm_info, u32.shm_info32, shm_rss);
1237		CP(u.shm_info, u32.shm_info32, shm_tot);
1238		CP(u.shm_info, u32.shm_info32, shm_swp);
1239		CP(u.shm_info, u32.shm_info32, swap_attempts);
1240		CP(u.shm_info, u32.shm_info32, swap_successes);
1241		error = copyout(&u32.shm_info32, uap->buf,
1242		    sizeof(u32.shm_info32));
1243		break;
1244	case SHM_STAT:
1245	case IPC_STAT:
1246		freebsd32_ipcperm_out(&u.shmid_ds.shm_perm,
1247		    &u32.shmid_ds32.shm_perm);
1248		if (u.shmid_ds.shm_segsz > INT32_MAX)
1249			u32.shmid_ds32.shm_segsz = INT32_MAX;
1250		else
1251			CP(u.shmid_ds, u32.shmid_ds32, shm_segsz);
1252		CP(u.shmid_ds, u32.shmid_ds32, shm_lpid);
1253		CP(u.shmid_ds, u32.shmid_ds32, shm_cpid);
1254		CP(u.shmid_ds, u32.shmid_ds32, shm_nattch);
1255		CP(u.shmid_ds, u32.shmid_ds32, shm_atime);
1256		CP(u.shmid_ds, u32.shmid_ds32, shm_dtime);
1257		CP(u.shmid_ds, u32.shmid_ds32, shm_ctime);
1258		error = copyout(&u32.shmid_ds32, uap->buf,
1259		    sizeof(u32.shmid_ds32));
1260		break;
1261	}
1262
1263done:
1264	if (error) {
1265		/* Invalidate the return value */
1266		td->td_retval[0] = -1;
1267	}
1268	return (error);
1269}
1270#endif
1271
1272#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1273    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1274
1275#ifndef CP
1276#define CP(src, dst, fld)	do { (dst).fld = (src).fld; } while (0)
1277#endif
1278
1279#ifndef _SYS_SYSPROTO_H_
1280struct freebsd7_shmctl_args {
1281	int shmid;
1282	int cmd;
1283	struct shmid_ds_old *buf;
1284};
1285#endif
1286int
1287freebsd7_shmctl(td, uap)
1288	struct thread *td;
1289	struct freebsd7_shmctl_args *uap;
1290{
1291	int error = 0;
1292	struct shmid_ds_old old;
1293	struct shmid_ds buf;
1294	size_t bufsz;
1295
1296	/*
1297	 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
1298	 * Linux binaries.  If we see the call come through the FreeBSD ABI,
1299	 * return an error back to the user since we do not to support this.
1300	 */
1301	if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
1302	    uap->cmd == SHM_STAT)
1303		return (EINVAL);
1304
1305	/* IPC_SET needs to copyin the buffer before calling kern_shmctl */
1306	if (uap->cmd == IPC_SET) {
1307		if ((error = copyin(uap->buf, &old, sizeof(old))))
1308			goto done;
1309		ipcperm_old2new(&old.shm_perm, &buf.shm_perm);
1310		CP(old, buf, shm_segsz);
1311		CP(old, buf, shm_lpid);
1312		CP(old, buf, shm_cpid);
1313		CP(old, buf, shm_nattch);
1314		CP(old, buf, shm_atime);
1315		CP(old, buf, shm_dtime);
1316		CP(old, buf, shm_ctime);
1317	}
1318
1319	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
1320	if (error)
1321		goto done;
1322
1323	/* Cases in which we need to copyout */
1324	switch (uap->cmd) {
1325	case IPC_STAT:
1326		ipcperm_new2old(&buf.shm_perm, &old.shm_perm);
1327		if (buf.shm_segsz > INT_MAX)
1328			old.shm_segsz = INT_MAX;
1329		else
1330			CP(buf, old, shm_segsz);
1331		CP(buf, old, shm_lpid);
1332		CP(buf, old, shm_cpid);
1333		if (buf.shm_nattch > SHRT_MAX)
1334			old.shm_nattch = SHRT_MAX;
1335		else
1336			CP(buf, old, shm_nattch);
1337		CP(buf, old, shm_atime);
1338		CP(buf, old, shm_dtime);
1339		CP(buf, old, shm_ctime);
1340		old.shm_internal = NULL;
1341		error = copyout(&old, uap->buf, sizeof(old));
1342		break;
1343	}
1344
1345done:
1346	if (error) {
1347		/* Invalidate the return value */
1348		td->td_retval[0] = -1;
1349	}
1350	return (error);
1351}
1352
1353#endif	/* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
1354	   COMPAT_FREEBSD7 */
1355
1356static int
1357sysvshm_modload(struct module *module, int cmd, void *arg)
1358{
1359	int error = 0;
1360
1361	switch (cmd) {
1362	case MOD_LOAD:
1363		error = shminit();
1364		if (error != 0)
1365			shmunload();
1366		break;
1367	case MOD_UNLOAD:
1368		error = shmunload();
1369		break;
1370	case MOD_SHUTDOWN:
1371		break;
1372	default:
1373		error = EINVAL;
1374		break;
1375	}
1376	return (error);
1377}
1378
1379static moduledata_t sysvshm_mod = {
1380	"sysvshm",
1381	&sysvshm_modload,
1382	NULL
1383};
1384
1385DECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
1386MODULE_VERSION(sysvshm, 1);
1387