sysv_shm.c revision 219028
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 219028 2011-02-25 10:11:01Z netchild $");
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}
250
251static int
252shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s)
253{
254	struct shmid_kernel *shmseg;
255	int segnum, result;
256	vm_size_t size;
257
258	GIANT_REQUIRED;
259
260	segnum = IPCID_TO_IX(shmmap_s->shmid);
261	shmseg = &shmsegs[segnum];
262	size = round_page(shmseg->u.shm_segsz);
263	result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size);
264	if (result != KERN_SUCCESS)
265		return (EINVAL);
266	shmmap_s->shmid = -1;
267	shmseg->u.shm_dtime = time_second;
268	if ((--shmseg->u.shm_nattch <= 0) &&
269	    (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) {
270		shm_deallocate_segment(shmseg);
271		shm_last_free = segnum;
272	}
273	return (0);
274}
275
276#ifndef _SYS_SYSPROTO_H_
277struct shmdt_args {
278	const void *shmaddr;
279};
280#endif
281int
282shmdt(td, uap)
283	struct thread *td;
284	struct shmdt_args *uap;
285{
286	struct proc *p = td->td_proc;
287	struct shmmap_state *shmmap_s;
288#ifdef MAC
289	struct shmid_kernel *shmsegptr;
290#endif
291	int i;
292	int error = 0;
293
294	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
295		return (ENOSYS);
296	mtx_lock(&Giant);
297	shmmap_s = p->p_vmspace->vm_shm;
298 	if (shmmap_s == NULL) {
299		error = EINVAL;
300		goto done2;
301	}
302	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) {
303		if (shmmap_s->shmid != -1 &&
304		    shmmap_s->va == (vm_offset_t)uap->shmaddr) {
305			break;
306		}
307	}
308	if (i == shminfo.shmseg) {
309		error = EINVAL;
310		goto done2;
311	}
312#ifdef MAC
313	shmsegptr = &shmsegs[IPCID_TO_IX(shmmap_s->shmid)];
314	error = mac_sysvshm_check_shmdt(td->td_ucred, shmsegptr);
315	if (error != 0)
316		goto done2;
317#endif
318	error = shm_delete_mapping(p->p_vmspace, shmmap_s);
319done2:
320	mtx_unlock(&Giant);
321	return (error);
322}
323
324#ifndef _SYS_SYSPROTO_H_
325struct shmat_args {
326	int shmid;
327	const void *shmaddr;
328	int shmflg;
329};
330#endif
331int
332kern_shmat(td, shmid, shmaddr, shmflg)
333	struct thread *td;
334	int shmid;
335	const void *shmaddr;
336	int shmflg;
337{
338	struct proc *p = td->td_proc;
339	int i, flags;
340	struct shmid_kernel *shmseg;
341	struct shmmap_state *shmmap_s = NULL;
342	vm_offset_t attach_va;
343	vm_prot_t prot;
344	vm_size_t size;
345	int rv;
346	int error = 0;
347
348	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
349		return (ENOSYS);
350	mtx_lock(&Giant);
351	shmmap_s = p->p_vmspace->vm_shm;
352	if (shmmap_s == NULL) {
353		shmmap_s = malloc(shminfo.shmseg * sizeof(struct shmmap_state),
354		    M_SHM, M_WAITOK);
355		for (i = 0; i < shminfo.shmseg; i++)
356			shmmap_s[i].shmid = -1;
357		p->p_vmspace->vm_shm = shmmap_s;
358	}
359	shmseg = shm_find_segment_by_shmid(shmid);
360	if (shmseg == NULL) {
361		error = EINVAL;
362		goto done2;
363	}
364	error = ipcperm(td, &shmseg->u.shm_perm,
365	    (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
366	if (error)
367		goto done2;
368#ifdef MAC
369	error = mac_sysvshm_check_shmat(td->td_ucred, shmseg, shmflg);
370	if (error != 0)
371		goto done2;
372#endif
373	for (i = 0; i < shminfo.shmseg; i++) {
374		if (shmmap_s->shmid == -1)
375			break;
376		shmmap_s++;
377	}
378	if (i >= shminfo.shmseg) {
379		error = EMFILE;
380		goto done2;
381	}
382	size = round_page(shmseg->u.shm_segsz);
383	prot = VM_PROT_READ;
384	if ((shmflg & SHM_RDONLY) == 0)
385		prot |= VM_PROT_WRITE;
386	flags = MAP_ANON | MAP_SHARED;
387	if (shmaddr) {
388		flags |= MAP_FIXED;
389		if (shmflg & SHM_RND) {
390			attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1);
391		} else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) {
392			attach_va = (vm_offset_t)shmaddr;
393		} else {
394			error = EINVAL;
395			goto done2;
396		}
397	} else {
398		/*
399		 * This is just a hint to vm_map_find() about where to
400		 * put it.
401		 */
402		PROC_LOCK(p);
403		attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr +
404		    lim_max(p, RLIMIT_DATA));
405		PROC_UNLOCK(p);
406	}
407
408	vm_object_reference(shmseg->object);
409	rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object,
410	    0, &attach_va, size, (flags & MAP_FIXED) ? VMFS_NO_SPACE :
411	    VMFS_ANY_SPACE, prot, prot, 0);
412	if (rv != KERN_SUCCESS) {
413		vm_object_deallocate(shmseg->object);
414		error = ENOMEM;
415		goto done2;
416	}
417	vm_map_inherit(&p->p_vmspace->vm_map,
418		attach_va, attach_va + size, VM_INHERIT_SHARE);
419
420	shmmap_s->va = attach_va;
421	shmmap_s->shmid = shmid;
422	shmseg->u.shm_lpid = p->p_pid;
423	shmseg->u.shm_atime = time_second;
424	shmseg->u.shm_nattch++;
425	td->td_retval[0] = attach_va;
426done2:
427	mtx_unlock(&Giant);
428	return (error);
429}
430
431int
432shmat(td, uap)
433	struct thread *td;
434	struct shmat_args *uap;
435{
436	return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg);
437}
438
439int
440kern_shmctl(td, shmid, cmd, buf, bufsz)
441	struct thread *td;
442	int shmid;
443	int cmd;
444	void *buf;
445	size_t *bufsz;
446{
447	int error = 0;
448	struct shmid_kernel *shmseg;
449
450	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
451		return (ENOSYS);
452
453	mtx_lock(&Giant);
454	switch (cmd) {
455	/*
456	 * It is possible that kern_shmctl is being called from the Linux ABI
457	 * layer, in which case, we will need to implement IPC_INFO.  It should
458	 * be noted that other shmctl calls will be funneled through here for
459	 * Linix binaries as well.
460	 *
461	 * NB: The Linux ABI layer will convert this data to structure(s) more
462	 * consistent with the Linux ABI.
463	 */
464	case IPC_INFO:
465		memcpy(buf, &shminfo, sizeof(shminfo));
466		if (bufsz)
467			*bufsz = sizeof(shminfo);
468		td->td_retval[0] = shmalloced;
469		goto done2;
470	case SHM_INFO: {
471		struct shm_info shm_info;
472		shm_info.used_ids = shm_nused;
473		shm_info.shm_rss = 0;	/*XXX where to get from ? */
474		shm_info.shm_tot = 0;	/*XXX where to get from ? */
475		shm_info.shm_swp = 0;	/*XXX where to get from ? */
476		shm_info.swap_attempts = 0;	/*XXX where to get from ? */
477		shm_info.swap_successes = 0;	/*XXX where to get from ? */
478		memcpy(buf, &shm_info, sizeof(shm_info));
479		if (bufsz)
480			*bufsz = sizeof(shm_info);
481		td->td_retval[0] = shmalloced;
482		goto done2;
483	}
484	}
485	if (cmd == SHM_STAT)
486		shmseg = shm_find_segment_by_shmidx(shmid);
487	else
488		shmseg = shm_find_segment_by_shmid(shmid);
489	if (shmseg == NULL) {
490		error = EINVAL;
491		goto done2;
492	}
493#ifdef MAC
494	error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd);
495	if (error != 0)
496		goto done2;
497#endif
498	switch (cmd) {
499	case SHM_STAT:
500	case IPC_STAT:
501		error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
502		if (error)
503			goto done2;
504		memcpy(buf, &shmseg->u, sizeof(struct shmid_ds));
505		if (bufsz)
506			*bufsz = sizeof(struct shmid_ds);
507		if (cmd == SHM_STAT)
508			td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->u.shm_perm);
509		break;
510	case IPC_SET: {
511		struct shmid_ds *shmid;
512
513		shmid = (struct shmid_ds *)buf;
514		error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
515		if (error)
516			goto done2;
517		shmseg->u.shm_perm.uid = shmid->shm_perm.uid;
518		shmseg->u.shm_perm.gid = shmid->shm_perm.gid;
519		shmseg->u.shm_perm.mode =
520		    (shmseg->u.shm_perm.mode & ~ACCESSPERMS) |
521		    (shmid->shm_perm.mode & ACCESSPERMS);
522		shmseg->u.shm_ctime = time_second;
523		break;
524	}
525	case IPC_RMID:
526		error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
527		if (error)
528			goto done2;
529		shmseg->u.shm_perm.key = IPC_PRIVATE;
530		shmseg->u.shm_perm.mode |= SHMSEG_REMOVED;
531		if (shmseg->u.shm_nattch <= 0) {
532			shm_deallocate_segment(shmseg);
533			shm_last_free = IPCID_TO_IX(shmid);
534		}
535		break;
536#if 0
537	case SHM_LOCK:
538	case SHM_UNLOCK:
539#endif
540	default:
541		error = EINVAL;
542		break;
543	}
544done2:
545	mtx_unlock(&Giant);
546	return (error);
547}
548
549#ifndef _SYS_SYSPROTO_H_
550struct shmctl_args {
551	int shmid;
552	int cmd;
553	struct shmid_ds *buf;
554};
555#endif
556int
557shmctl(td, uap)
558	struct thread *td;
559	struct shmctl_args *uap;
560{
561	int error = 0;
562	struct shmid_ds buf;
563	size_t bufsz;
564
565	/*
566	 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
567	 * Linux binaries.  If we see the call come through the FreeBSD ABI,
568	 * return an error back to the user since we do not to support this.
569	 */
570	if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
571	    uap->cmd == SHM_STAT)
572		return (EINVAL);
573
574	/* IPC_SET needs to copyin the buffer before calling kern_shmctl */
575	if (uap->cmd == IPC_SET) {
576		if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds))))
577			goto done;
578	}
579
580	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
581	if (error)
582		goto done;
583
584	/* Cases in which we need to copyout */
585	switch (uap->cmd) {
586	case IPC_STAT:
587		error = copyout(&buf, uap->buf, bufsz);
588		break;
589	}
590
591done:
592	if (error) {
593		/* Invalidate the return value */
594		td->td_retval[0] = -1;
595	}
596	return (error);
597}
598
599
600static int
601shmget_existing(td, uap, mode, segnum)
602	struct thread *td;
603	struct shmget_args *uap;
604	int mode;
605	int segnum;
606{
607	struct shmid_kernel *shmseg;
608	int error;
609
610	shmseg = &shmsegs[segnum];
611	if (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) {
612		/*
613		 * This segment is in the process of being allocated.  Wait
614		 * until it's done, and look the key up again (in case the
615		 * allocation failed or it was freed).
616		 */
617		shmseg->u.shm_perm.mode |= SHMSEG_WANTED;
618		error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0);
619		if (error)
620			return (error);
621		return (EAGAIN);
622	}
623	if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
624		return (EEXIST);
625#ifdef MAC
626	error = mac_sysvshm_check_shmget(td->td_ucred, shmseg, uap->shmflg);
627	if (error != 0)
628		return (error);
629#endif
630	if (uap->size != 0 && uap->size > shmseg->u.shm_segsz)
631		return (EINVAL);
632	td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
633	return (0);
634}
635
636static int
637shmget_allocate_segment(td, uap, mode)
638	struct thread *td;
639	struct shmget_args *uap;
640	int mode;
641{
642	int i, segnum, shmid;
643	size_t size;
644	struct ucred *cred = td->td_ucred;
645	struct shmid_kernel *shmseg;
646	vm_object_t shm_object;
647
648	GIANT_REQUIRED;
649
650	if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
651		return (EINVAL);
652	if (shm_nused >= shminfo.shmmni) /* Any shmids left? */
653		return (ENOSPC);
654	size = round_page(uap->size);
655	if (shm_committed + btoc(size) > shminfo.shmall)
656		return (ENOMEM);
657	if (shm_last_free < 0) {
658		shmrealloc();	/* Maybe expand the shmsegs[] array. */
659		for (i = 0; i < shmalloced; i++)
660			if (shmsegs[i].u.shm_perm.mode & SHMSEG_FREE)
661				break;
662		if (i == shmalloced)
663			return (ENOSPC);
664		segnum = i;
665	} else  {
666		segnum = shm_last_free;
667		shm_last_free = -1;
668	}
669	shmseg = &shmsegs[segnum];
670	/*
671	 * In case we sleep in malloc(), mark the segment present but deleted
672	 * so that noone else tries to create the same key.
673	 */
674	shmseg->u.shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
675	shmseg->u.shm_perm.key = uap->key;
676	shmseg->u.shm_perm.seq = (shmseg->u.shm_perm.seq + 1) & 0x7fff;
677	shmid = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
678
679	/*
680	 * We make sure that we have allocated a pager before we need
681	 * to.
682	 */
683	shm_object = vm_pager_allocate(shm_use_phys ? OBJT_PHYS : OBJT_SWAP,
684	    0, size, VM_PROT_DEFAULT, 0, cred);
685	if (shm_object == NULL)
686		return (ENOMEM);
687	VM_OBJECT_LOCK(shm_object);
688	vm_object_clear_flag(shm_object, OBJ_ONEMAPPING);
689	vm_object_set_flag(shm_object, OBJ_NOSPLIT);
690	VM_OBJECT_UNLOCK(shm_object);
691
692	shmseg->object = shm_object;
693	shmseg->u.shm_perm.cuid = shmseg->u.shm_perm.uid = cred->cr_uid;
694	shmseg->u.shm_perm.cgid = shmseg->u.shm_perm.gid = cred->cr_gid;
695	shmseg->u.shm_perm.mode = (shmseg->u.shm_perm.mode & SHMSEG_WANTED) |
696	    (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
697	shmseg->u.shm_segsz = uap->size;
698	shmseg->u.shm_cpid = td->td_proc->p_pid;
699	shmseg->u.shm_lpid = shmseg->u.shm_nattch = 0;
700	shmseg->u.shm_atime = shmseg->u.shm_dtime = 0;
701#ifdef MAC
702	mac_sysvshm_create(cred, shmseg);
703#endif
704	shmseg->u.shm_ctime = time_second;
705	shm_committed += btoc(size);
706	shm_nused++;
707	if (shmseg->u.shm_perm.mode & SHMSEG_WANTED) {
708		/*
709		 * Somebody else wanted this key while we were asleep.  Wake
710		 * them up now.
711		 */
712		shmseg->u.shm_perm.mode &= ~SHMSEG_WANTED;
713		wakeup(shmseg);
714	}
715	td->td_retval[0] = shmid;
716	return (0);
717}
718
719#ifndef _SYS_SYSPROTO_H_
720struct shmget_args {
721	key_t key;
722	size_t size;
723	int shmflg;
724};
725#endif
726int
727shmget(td, uap)
728	struct thread *td;
729	struct shmget_args *uap;
730{
731	int segnum, mode;
732	int error;
733
734	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
735		return (ENOSYS);
736	mtx_lock(&Giant);
737	mode = uap->shmflg & ACCESSPERMS;
738	if (uap->key != IPC_PRIVATE) {
739	again:
740		segnum = shm_find_segment_by_key(uap->key);
741		if (segnum >= 0) {
742			error = shmget_existing(td, uap, mode, segnum);
743			if (error == EAGAIN)
744				goto again;
745			goto done2;
746		}
747		if ((uap->shmflg & IPC_CREAT) == 0) {
748			error = ENOENT;
749			goto done2;
750		}
751	}
752	error = shmget_allocate_segment(td, uap, mode);
753done2:
754	mtx_unlock(&Giant);
755	return (error);
756}
757
758static void
759shmfork_myhook(p1, p2)
760	struct proc *p1, *p2;
761{
762	struct shmmap_state *shmmap_s;
763	size_t size;
764	int i;
765
766	mtx_lock(&Giant);
767	size = shminfo.shmseg * sizeof(struct shmmap_state);
768	shmmap_s = malloc(size, M_SHM, M_WAITOK);
769	bcopy(p1->p_vmspace->vm_shm, shmmap_s, size);
770	p2->p_vmspace->vm_shm = shmmap_s;
771	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
772		if (shmmap_s->shmid != -1)
773			shmsegs[IPCID_TO_IX(shmmap_s->shmid)].u.shm_nattch++;
774	mtx_unlock(&Giant);
775}
776
777static void
778shmexit_myhook(struct vmspace *vm)
779{
780	struct shmmap_state *base, *shm;
781	int i;
782
783	if ((base = vm->vm_shm) != NULL) {
784		vm->vm_shm = NULL;
785		mtx_lock(&Giant);
786		for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) {
787			if (shm->shmid != -1)
788				shm_delete_mapping(vm, shm);
789		}
790		mtx_unlock(&Giant);
791		free(base, M_SHM);
792	}
793}
794
795static void
796shmrealloc(void)
797{
798	int i;
799	struct shmid_kernel *newsegs;
800
801	if (shmalloced >= shminfo.shmmni)
802		return;
803
804	newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK);
805	if (newsegs == NULL)
806		return;
807	for (i = 0; i < shmalloced; i++)
808		bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
809	for (; i < shminfo.shmmni; i++) {
810		shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
811		shmsegs[i].u.shm_perm.seq = 0;
812#ifdef MAC
813		mac_sysvshm_init(&shmsegs[i]);
814#endif
815	}
816	free(shmsegs, M_SHM);
817	shmsegs = newsegs;
818	shmalloced = shminfo.shmmni;
819}
820
821static struct syscall_helper_data shm_syscalls[] = {
822	SYSCALL_INIT_HELPER(shmat),
823	SYSCALL_INIT_HELPER(shmctl),
824	SYSCALL_INIT_HELPER(shmdt),
825	SYSCALL_INIT_HELPER(shmget),
826#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
827    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
828	SYSCALL_INIT_HELPER(freebsd7_shmctl),
829#endif
830#if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
831	SYSCALL_INIT_HELPER(shmsys),
832#endif
833	SYSCALL_INIT_LAST
834};
835
836#ifdef COMPAT_FREEBSD32
837#include <compat/freebsd32/freebsd32.h>
838#include <compat/freebsd32/freebsd32_ipc.h>
839#include <compat/freebsd32/freebsd32_proto.h>
840#include <compat/freebsd32/freebsd32_signal.h>
841#include <compat/freebsd32/freebsd32_syscall.h>
842#include <compat/freebsd32/freebsd32_util.h>
843
844static struct syscall_helper_data shm32_syscalls[] = {
845	SYSCALL32_INIT_HELPER(shmat),
846	SYSCALL32_INIT_HELPER(shmdt),
847	SYSCALL32_INIT_HELPER(shmget),
848	SYSCALL32_INIT_HELPER(freebsd32_shmsys),
849	SYSCALL32_INIT_HELPER(freebsd32_shmctl),
850#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
851    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
852	SYSCALL32_INIT_HELPER(freebsd7_freebsd32_shmctl),
853#endif
854	SYSCALL_INIT_LAST
855};
856#endif
857
858static int
859shminit()
860{
861	int i, error;
862
863#ifndef BURN_BRIDGES
864	if (TUNABLE_ULONG_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall) != 0)
865		printf("kern.ipc.shmmaxpgs is now called kern.ipc.shmall!\n");
866#endif
867	TUNABLE_ULONG_FETCH("kern.ipc.shmall", &shminfo.shmall);
868
869	/* Initialize shmmax dealing with possible overflow. */
870	for (i = PAGE_SIZE; i > 0; i--) {
871		shminfo.shmmax = shminfo.shmall * i;
872		if (shminfo.shmmax >= shminfo.shmall)
873			break;
874	}
875
876	TUNABLE_ULONG_FETCH("kern.ipc.shmmin", &shminfo.shmmin);
877	TUNABLE_ULONG_FETCH("kern.ipc.shmmni", &shminfo.shmmni);
878	TUNABLE_ULONG_FETCH("kern.ipc.shmseg", &shminfo.shmseg);
879	TUNABLE_INT_FETCH("kern.ipc.shm_use_phys", &shm_use_phys);
880
881	shmalloced = shminfo.shmmni;
882	shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK);
883	for (i = 0; i < shmalloced; i++) {
884		shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
885		shmsegs[i].u.shm_perm.seq = 0;
886#ifdef MAC
887		mac_sysvshm_init(&shmsegs[i]);
888#endif
889	}
890	shm_last_free = 0;
891	shm_nused = 0;
892	shm_committed = 0;
893	shmexit_hook = &shmexit_myhook;
894	shmfork_hook = &shmfork_myhook;
895
896	error = syscall_helper_register(shm_syscalls);
897	if (error != 0)
898		return (error);
899#ifdef COMPAT_FREEBSD32
900	error = syscall32_helper_register(shm32_syscalls);
901	if (error != 0)
902		return (error);
903#endif
904	return (0);
905}
906
907static int
908shmunload()
909{
910	int i;
911
912	if (shm_nused > 0)
913		return (EBUSY);
914
915#ifdef COMPAT_FREEBSD32
916	syscall32_helper_unregister(shm32_syscalls);
917#endif
918	syscall_helper_unregister(shm_syscalls);
919
920	for (i = 0; i < shmalloced; i++) {
921#ifdef MAC
922		mac_sysvshm_destroy(&shmsegs[i]);
923#endif
924		/*
925		 * Objects might be still mapped into the processes
926		 * address spaces.  Actual free would happen on the
927		 * last mapping destruction.
928		 */
929		if (shmsegs[i].u.shm_perm.mode != SHMSEG_FREE)
930			vm_object_deallocate(shmsegs[i].object);
931	}
932	free(shmsegs, M_SHM);
933	shmexit_hook = NULL;
934	shmfork_hook = NULL;
935	return (0);
936}
937
938static int
939sysctl_shmsegs(SYSCTL_HANDLER_ARGS)
940{
941
942	return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0])));
943}
944
945#if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
946struct oshmid_ds {
947	struct	ipc_perm_old shm_perm;	/* operation perms */
948	int	shm_segsz;		/* size of segment (bytes) */
949	u_short	shm_cpid;		/* pid, creator */
950	u_short	shm_lpid;		/* pid, last operation */
951	short	shm_nattch;		/* no. of current attaches */
952	time_t	shm_atime;		/* last attach time */
953	time_t	shm_dtime;		/* last detach time */
954	time_t	shm_ctime;		/* last change time */
955	void	*shm_handle;		/* internal handle for shm segment */
956};
957
958struct oshmctl_args {
959	int shmid;
960	int cmd;
961	struct oshmid_ds *ubuf;
962};
963
964static int
965oshmctl(struct thread *td, struct oshmctl_args *uap)
966{
967#ifdef COMPAT_43
968	int error = 0;
969	struct shmid_kernel *shmseg;
970	struct oshmid_ds outbuf;
971
972	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
973		return (ENOSYS);
974	mtx_lock(&Giant);
975	shmseg = shm_find_segment_by_shmid(uap->shmid);
976	if (shmseg == NULL) {
977		error = EINVAL;
978		goto done2;
979	}
980	switch (uap->cmd) {
981	case IPC_STAT:
982		error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
983		if (error)
984			goto done2;
985#ifdef MAC
986		error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, uap->cmd);
987		if (error != 0)
988			goto done2;
989#endif
990		ipcperm_new2old(&shmseg->u.shm_perm, &outbuf.shm_perm);
991		outbuf.shm_segsz = shmseg->u.shm_segsz;
992		outbuf.shm_cpid = shmseg->u.shm_cpid;
993		outbuf.shm_lpid = shmseg->u.shm_lpid;
994		outbuf.shm_nattch = shmseg->u.shm_nattch;
995		outbuf.shm_atime = shmseg->u.shm_atime;
996		outbuf.shm_dtime = shmseg->u.shm_dtime;
997		outbuf.shm_ctime = shmseg->u.shm_ctime;
998		outbuf.shm_handle = shmseg->object;
999		error = copyout(&outbuf, uap->ubuf, sizeof(outbuf));
1000		if (error)
1001			goto done2;
1002		break;
1003	default:
1004		error = freebsd7_shmctl(td, (struct freebsd7_shmctl_args *)uap);
1005		break;
1006	}
1007done2:
1008	mtx_unlock(&Giant);
1009	return (error);
1010#else
1011	return (EINVAL);
1012#endif
1013}
1014
1015/* XXX casting to (sy_call_t *) is bogus, as usual. */
1016static sy_call_t *shmcalls[] = {
1017	(sy_call_t *)shmat, (sy_call_t *)oshmctl,
1018	(sy_call_t *)shmdt, (sy_call_t *)shmget,
1019	(sy_call_t *)freebsd7_shmctl
1020};
1021
1022int
1023shmsys(td, uap)
1024	struct thread *td;
1025	/* XXX actually varargs. */
1026	struct shmsys_args /* {
1027		int	which;
1028		int	a2;
1029		int	a3;
1030		int	a4;
1031	} */ *uap;
1032{
1033	int error;
1034
1035	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
1036		return (ENOSYS);
1037	if (uap->which < 0 ||
1038	    uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
1039		return (EINVAL);
1040	mtx_lock(&Giant);
1041	error = (*shmcalls[uap->which])(td, &uap->a2);
1042	mtx_unlock(&Giant);
1043	return (error);
1044}
1045
1046#endif	/* i386 && (COMPAT_FREEBSD4 || COMPAT_43) */
1047
1048#ifdef COMPAT_FREEBSD32
1049
1050int
1051freebsd32_shmsys(struct thread *td, struct freebsd32_shmsys_args *uap)
1052{
1053
1054#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1055    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1056	switch (uap->which) {
1057	case 0:	{	/* shmat */
1058		struct shmat_args ap;
1059
1060		ap.shmid = uap->a2;
1061		ap.shmaddr = PTRIN(uap->a3);
1062		ap.shmflg = uap->a4;
1063		return (sysent[SYS_shmat].sy_call(td, &ap));
1064	}
1065	case 2: {	/* shmdt */
1066		struct shmdt_args ap;
1067
1068		ap.shmaddr = PTRIN(uap->a2);
1069		return (sysent[SYS_shmdt].sy_call(td, &ap));
1070	}
1071	case 3: {	/* shmget */
1072		struct shmget_args ap;
1073
1074		ap.key = uap->a2;
1075		ap.size = uap->a3;
1076		ap.shmflg = uap->a4;
1077		return (sysent[SYS_shmget].sy_call(td, &ap));
1078	}
1079	case 4: {	/* shmctl */
1080		struct freebsd7_freebsd32_shmctl_args ap;
1081
1082		ap.shmid = uap->a2;
1083		ap.cmd = uap->a3;
1084		ap.buf = PTRIN(uap->a4);
1085		return (freebsd7_freebsd32_shmctl(td, &ap));
1086	}
1087	case 1:		/* oshmctl */
1088	default:
1089		return (EINVAL);
1090	}
1091#else
1092	return (nosys(td, NULL));
1093#endif
1094}
1095
1096#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1097    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1098int
1099freebsd7_freebsd32_shmctl(struct thread *td,
1100    struct freebsd7_freebsd32_shmctl_args *uap)
1101{
1102	int error = 0;
1103	union {
1104		struct shmid_ds shmid_ds;
1105		struct shm_info shm_info;
1106		struct shminfo shminfo;
1107	} u;
1108	union {
1109		struct shmid_ds32_old shmid_ds32;
1110		struct shm_info32 shm_info32;
1111		struct shminfo32 shminfo32;
1112	} u32;
1113	size_t sz;
1114
1115	if (uap->cmd == IPC_SET) {
1116		if ((error = copyin(uap->buf, &u32.shmid_ds32,
1117		    sizeof(u32.shmid_ds32))))
1118			goto done;
1119		freebsd32_ipcperm_old_in(&u32.shmid_ds32.shm_perm,
1120		    &u.shmid_ds.shm_perm);
1121		CP(u32.shmid_ds32, u.shmid_ds, shm_segsz);
1122		CP(u32.shmid_ds32, u.shmid_ds, shm_lpid);
1123		CP(u32.shmid_ds32, u.shmid_ds, shm_cpid);
1124		CP(u32.shmid_ds32, u.shmid_ds, shm_nattch);
1125		CP(u32.shmid_ds32, u.shmid_ds, shm_atime);
1126		CP(u32.shmid_ds32, u.shmid_ds, shm_dtime);
1127		CP(u32.shmid_ds32, u.shmid_ds, shm_ctime);
1128	}
1129
1130	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz);
1131	if (error)
1132		goto done;
1133
1134	/* Cases in which we need to copyout */
1135	switch (uap->cmd) {
1136	case IPC_INFO:
1137		CP(u.shminfo, u32.shminfo32, shmmax);
1138		CP(u.shminfo, u32.shminfo32, shmmin);
1139		CP(u.shminfo, u32.shminfo32, shmmni);
1140		CP(u.shminfo, u32.shminfo32, shmseg);
1141		CP(u.shminfo, u32.shminfo32, shmall);
1142		error = copyout(&u32.shminfo32, uap->buf,
1143		    sizeof(u32.shminfo32));
1144		break;
1145	case SHM_INFO:
1146		CP(u.shm_info, u32.shm_info32, used_ids);
1147		CP(u.shm_info, u32.shm_info32, shm_rss);
1148		CP(u.shm_info, u32.shm_info32, shm_tot);
1149		CP(u.shm_info, u32.shm_info32, shm_swp);
1150		CP(u.shm_info, u32.shm_info32, swap_attempts);
1151		CP(u.shm_info, u32.shm_info32, swap_successes);
1152		error = copyout(&u32.shm_info32, uap->buf,
1153		    sizeof(u32.shm_info32));
1154		break;
1155	case SHM_STAT:
1156	case IPC_STAT:
1157		freebsd32_ipcperm_old_out(&u.shmid_ds.shm_perm,
1158		    &u32.shmid_ds32.shm_perm);
1159		if (u.shmid_ds.shm_segsz > INT32_MAX)
1160			u32.shmid_ds32.shm_segsz = INT32_MAX;
1161		else
1162			CP(u.shmid_ds, u32.shmid_ds32, shm_segsz);
1163		CP(u.shmid_ds, u32.shmid_ds32, shm_lpid);
1164		CP(u.shmid_ds, u32.shmid_ds32, shm_cpid);
1165		CP(u.shmid_ds, u32.shmid_ds32, shm_nattch);
1166		CP(u.shmid_ds, u32.shmid_ds32, shm_atime);
1167		CP(u.shmid_ds, u32.shmid_ds32, shm_dtime);
1168		CP(u.shmid_ds, u32.shmid_ds32, shm_ctime);
1169		u32.shmid_ds32.shm_internal = 0;
1170		error = copyout(&u32.shmid_ds32, uap->buf,
1171		    sizeof(u32.shmid_ds32));
1172		break;
1173	}
1174
1175done:
1176	if (error) {
1177		/* Invalidate the return value */
1178		td->td_retval[0] = -1;
1179	}
1180	return (error);
1181}
1182#endif
1183
1184int
1185freebsd32_shmctl(struct thread *td, struct freebsd32_shmctl_args *uap)
1186{
1187	int error = 0;
1188	union {
1189		struct shmid_ds shmid_ds;
1190		struct shm_info shm_info;
1191		struct shminfo shminfo;
1192	} u;
1193	union {
1194		struct shmid_ds32 shmid_ds32;
1195		struct shm_info32 shm_info32;
1196		struct shminfo32 shminfo32;
1197	} u32;
1198	size_t sz;
1199
1200	if (uap->cmd == IPC_SET) {
1201		if ((error = copyin(uap->buf, &u32.shmid_ds32,
1202		    sizeof(u32.shmid_ds32))))
1203			goto done;
1204		freebsd32_ipcperm_in(&u32.shmid_ds32.shm_perm,
1205		    &u.shmid_ds.shm_perm);
1206		CP(u32.shmid_ds32, u.shmid_ds, shm_segsz);
1207		CP(u32.shmid_ds32, u.shmid_ds, shm_lpid);
1208		CP(u32.shmid_ds32, u.shmid_ds, shm_cpid);
1209		CP(u32.shmid_ds32, u.shmid_ds, shm_nattch);
1210		CP(u32.shmid_ds32, u.shmid_ds, shm_atime);
1211		CP(u32.shmid_ds32, u.shmid_ds, shm_dtime);
1212		CP(u32.shmid_ds32, u.shmid_ds, shm_ctime);
1213	}
1214
1215	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz);
1216	if (error)
1217		goto done;
1218
1219	/* Cases in which we need to copyout */
1220	switch (uap->cmd) {
1221	case IPC_INFO:
1222		CP(u.shminfo, u32.shminfo32, shmmax);
1223		CP(u.shminfo, u32.shminfo32, shmmin);
1224		CP(u.shminfo, u32.shminfo32, shmmni);
1225		CP(u.shminfo, u32.shminfo32, shmseg);
1226		CP(u.shminfo, u32.shminfo32, shmall);
1227		error = copyout(&u32.shminfo32, uap->buf,
1228		    sizeof(u32.shminfo32));
1229		break;
1230	case SHM_INFO:
1231		CP(u.shm_info, u32.shm_info32, used_ids);
1232		CP(u.shm_info, u32.shm_info32, shm_rss);
1233		CP(u.shm_info, u32.shm_info32, shm_tot);
1234		CP(u.shm_info, u32.shm_info32, shm_swp);
1235		CP(u.shm_info, u32.shm_info32, swap_attempts);
1236		CP(u.shm_info, u32.shm_info32, swap_successes);
1237		error = copyout(&u32.shm_info32, uap->buf,
1238		    sizeof(u32.shm_info32));
1239		break;
1240	case SHM_STAT:
1241	case IPC_STAT:
1242		freebsd32_ipcperm_out(&u.shmid_ds.shm_perm,
1243		    &u32.shmid_ds32.shm_perm);
1244		if (u.shmid_ds.shm_segsz > INT32_MAX)
1245			u32.shmid_ds32.shm_segsz = INT32_MAX;
1246		else
1247			CP(u.shmid_ds, u32.shmid_ds32, shm_segsz);
1248		CP(u.shmid_ds, u32.shmid_ds32, shm_lpid);
1249		CP(u.shmid_ds, u32.shmid_ds32, shm_cpid);
1250		CP(u.shmid_ds, u32.shmid_ds32, shm_nattch);
1251		CP(u.shmid_ds, u32.shmid_ds32, shm_atime);
1252		CP(u.shmid_ds, u32.shmid_ds32, shm_dtime);
1253		CP(u.shmid_ds, u32.shmid_ds32, shm_ctime);
1254		error = copyout(&u32.shmid_ds32, uap->buf,
1255		    sizeof(u32.shmid_ds32));
1256		break;
1257	}
1258
1259done:
1260	if (error) {
1261		/* Invalidate the return value */
1262		td->td_retval[0] = -1;
1263	}
1264	return (error);
1265}
1266#endif
1267
1268#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1269    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1270
1271#ifndef CP
1272#define CP(src, dst, fld)	do { (dst).fld = (src).fld; } while (0)
1273#endif
1274
1275#ifndef _SYS_SYSPROTO_H_
1276struct freebsd7_shmctl_args {
1277	int shmid;
1278	int cmd;
1279	struct shmid_ds_old *buf;
1280};
1281#endif
1282int
1283freebsd7_shmctl(td, uap)
1284	struct thread *td;
1285	struct freebsd7_shmctl_args *uap;
1286{
1287	int error = 0;
1288	struct shmid_ds_old old;
1289	struct shmid_ds buf;
1290	size_t bufsz;
1291
1292	/*
1293	 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
1294	 * Linux binaries.  If we see the call come through the FreeBSD ABI,
1295	 * return an error back to the user since we do not to support this.
1296	 */
1297	if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
1298	    uap->cmd == SHM_STAT)
1299		return (EINVAL);
1300
1301	/* IPC_SET needs to copyin the buffer before calling kern_shmctl */
1302	if (uap->cmd == IPC_SET) {
1303		if ((error = copyin(uap->buf, &old, sizeof(old))))
1304			goto done;
1305		ipcperm_old2new(&old.shm_perm, &buf.shm_perm);
1306		CP(old, buf, shm_segsz);
1307		CP(old, buf, shm_lpid);
1308		CP(old, buf, shm_cpid);
1309		CP(old, buf, shm_nattch);
1310		CP(old, buf, shm_atime);
1311		CP(old, buf, shm_dtime);
1312		CP(old, buf, shm_ctime);
1313	}
1314
1315	error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
1316	if (error)
1317		goto done;
1318
1319	/* Cases in which we need to copyout */
1320	switch (uap->cmd) {
1321	case IPC_STAT:
1322		ipcperm_new2old(&buf.shm_perm, &old.shm_perm);
1323		if (buf.shm_segsz > INT_MAX)
1324			old.shm_segsz = INT_MAX;
1325		else
1326			CP(buf, old, shm_segsz);
1327		CP(buf, old, shm_lpid);
1328		CP(buf, old, shm_cpid);
1329		if (buf.shm_nattch > SHRT_MAX)
1330			old.shm_nattch = SHRT_MAX;
1331		else
1332			CP(buf, old, shm_nattch);
1333		CP(buf, old, shm_atime);
1334		CP(buf, old, shm_dtime);
1335		CP(buf, old, shm_ctime);
1336		old.shm_internal = NULL;
1337		error = copyout(&old, uap->buf, sizeof(old));
1338		break;
1339	}
1340
1341done:
1342	if (error) {
1343		/* Invalidate the return value */
1344		td->td_retval[0] = -1;
1345	}
1346	return (error);
1347}
1348
1349#endif	/* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
1350	   COMPAT_FREEBSD7 */
1351
1352static int
1353sysvshm_modload(struct module *module, int cmd, void *arg)
1354{
1355	int error = 0;
1356
1357	switch (cmd) {
1358	case MOD_LOAD:
1359		error = shminit();
1360		if (error != 0)
1361			shmunload();
1362		break;
1363	case MOD_UNLOAD:
1364		error = shmunload();
1365		break;
1366	case MOD_SHUTDOWN:
1367		break;
1368	default:
1369		error = EINVAL;
1370		break;
1371	}
1372	return (error);
1373}
1374
1375static moduledata_t sysvshm_mod = {
1376	"sysvshm",
1377	&sysvshm_modload,
1378	NULL
1379};
1380
1381DECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
1382MODULE_VERSION(sysvshm, 1);
1383