xref: /freebsd/sys/kern/sysv_shm.c (revision aa0a1e58f0189b0fde359a8bda032887e72057fa)
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$");
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 
98 FEATURE(sysv_shm, "System V shared memory segments support");
99 
100 static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
101 
102 static int shmget_allocate_segment(struct thread *td,
103     struct shmget_args *uap, int mode);
104 static 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 
112 static int shm_last_free, shm_nused, shmalloced;
113 vm_size_t shm_committed;
114 static struct shmid_kernel	*shmsegs;
115 
116 struct shmmap_state {
117 	vm_offset_t va;
118 	int shmid;
119 };
120 
121 static void shm_deallocate_segment(struct shmid_kernel *);
122 static int shm_find_segment_by_key(key_t);
123 static struct shmid_kernel *shm_find_segment_by_shmid(int);
124 static struct shmid_kernel *shm_find_segment_by_shmidx(int);
125 static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *);
126 static void shmrealloc(void);
127 static int shminit(void);
128 static int sysvshm_modload(struct module *, int, void *);
129 static int shmunload(void);
130 static void shmexit_myhook(struct vmspace *vm);
131 static void shmfork_myhook(struct proc *p1, struct proc *p2);
132 static 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 
156 struct	shminfo shminfo = {
157 	SHMMAX,
158 	SHMMIN,
159 	SHMMNI,
160 	SHMSEG,
161 	SHMALL
162 };
163 
164 static int shm_use_phys;
165 static int shm_allow_removed;
166 
167 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0,
168     "Maximum shared memory segment size");
169 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0,
170     "Minimum shared memory segment size");
171 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0,
172     "Number of shared memory identifiers");
173 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0,
174     "Number of segments per process");
175 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0,
176     "Maximum number of pages available for shared memory");
177 SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW,
178     &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core");
179 SYSCTL_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");
182 SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLTYPE_OPAQUE | CTLFLAG_RD,
183     NULL, 0, sysctl_shmsegs, "",
184     "Current number of shared memory segments allocated");
185 
186 static int
187 shm_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 
199 static struct shmid_kernel *
200 shm_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 
217 static struct shmid_kernel *
218 shm_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 
232 static void
233 shm_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 
251 static int
252 shm_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_
277 struct shmdt_args {
278 	const void *shmaddr;
279 };
280 #endif
281 int
282 shmdt(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);
319 done2:
320 	mtx_unlock(&Giant);
321 	return (error);
322 }
323 
324 #ifndef _SYS_SYSPROTO_H_
325 struct shmat_args {
326 	int shmid;
327 	const void *shmaddr;
328 	int shmflg;
329 };
330 #endif
331 int
332 kern_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;
426 done2:
427 	mtx_unlock(&Giant);
428 	return (error);
429 }
430 
431 int
432 shmat(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 
439 int
440 kern_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 	}
544 done2:
545 	mtx_unlock(&Giant);
546 	return (error);
547 }
548 
549 #ifndef _SYS_SYSPROTO_H_
550 struct shmctl_args {
551 	int shmid;
552 	int cmd;
553 	struct shmid_ds *buf;
554 };
555 #endif
556 int
557 shmctl(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 
591 done:
592 	if (error) {
593 		/* Invalidate the return value */
594 		td->td_retval[0] = -1;
595 	}
596 	return (error);
597 }
598 
599 
600 static int
601 shmget_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 
636 static int
637 shmget_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_
720 struct shmget_args {
721 	key_t key;
722 	size_t size;
723 	int shmflg;
724 };
725 #endif
726 int
727 shmget(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);
753 done2:
754 	mtx_unlock(&Giant);
755 	return (error);
756 }
757 
758 static void
759 shmfork_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 
777 static void
778 shmexit_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 
795 static void
796 shmrealloc(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 
821 static 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 
844 static 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 
858 static int
859 shminit()
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 
907 static int
908 shmunload()
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 
938 static int
939 sysctl_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))
946 struct 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 
958 struct oshmctl_args {
959 	int shmid;
960 	int cmd;
961 	struct oshmid_ds *ubuf;
962 };
963 
964 static int
965 oshmctl(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 	}
1007 done2:
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. */
1016 static 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 
1022 int
1023 shmsys(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 
1050 int
1051 freebsd32_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)
1098 int
1099 freebsd7_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 
1175 done:
1176 	if (error) {
1177 		/* Invalidate the return value */
1178 		td->td_retval[0] = -1;
1179 	}
1180 	return (error);
1181 }
1182 #endif
1183 
1184 int
1185 freebsd32_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 
1259 done:
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_
1276 struct freebsd7_shmctl_args {
1277 	int shmid;
1278 	int cmd;
1279 	struct shmid_ds_old *buf;
1280 };
1281 #endif
1282 int
1283 freebsd7_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 
1341 done:
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 
1352 static int
1353 sysvshm_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 
1375 static moduledata_t sysvshm_mod = {
1376 	"sysvshm",
1377 	&sysvshm_modload,
1378 	NULL
1379 };
1380 
1381 DECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
1382 MODULE_VERSION(sysvshm, 1);
1383