1/*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 *	Copyright (c) 1990, 1996-1998 Apple Computer, Inc.
30 *	All Rights Reserved.
31 */
32/*
33 * posix_shm.c : Support for POSIX shared memory APIs
34 *
35 *	File:	posix_shm.c
36 *	Author:	Ananthakrishna Ramesh
37 *
38 * HISTORY
39 * 2-Sep-1999	A.Ramesh
40 *	Created for MacOSX
41 *
42 */
43/*
44 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
45 * support for mandatory and extensible security protections.  This notice
46 * is included in support of clause 2.2 (b) of the Apple Public License,
47 * Version 2.0.
48 */
49
50#include <sys/cdefs.h>
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/kernel.h>
54#include <sys/file_internal.h>
55#include <sys/filedesc.h>
56#include <sys/stat.h>
57#include <sys/proc_internal.h>
58#include <sys/kauth.h>
59#include <sys/mount.h>
60#include <sys/namei.h>
61#include <sys/vnode.h>
62#include <sys/vnode_internal.h>
63#include <sys/ioctl.h>
64#include <sys/tty.h>
65#include <sys/malloc.h>
66#include <sys/mman.h>
67#include <sys/stat.h>
68#include <sys/sysproto.h>
69#include <sys/proc_info.h>
70#include <security/audit/audit.h>
71
72#if CONFIG_MACF
73#include <security/mac_framework.h>
74#endif
75
76#include <mach/mach_types.h>
77#include <mach/mach_vm.h>
78#include <mach/vm_map.h>
79#include <mach/vm_prot.h>
80#include <mach/vm_inherit.h>
81#include <mach/kern_return.h>
82#include <mach/memory_object_control.h>
83
84#include <vm/vm_map.h>
85#include <vm/vm_protos.h>
86
87#define f_flag f_fglob->fg_flag
88#define f_type f_fglob->fg_ops->fo_type
89#define f_msgcount f_fglob->fg_msgcount
90#define f_cred f_fglob->fg_cred
91#define f_ops f_fglob->fg_ops
92#define f_offset f_fglob->fg_offset
93#define f_data f_fglob->fg_data
94#define	PSHMNAMLEN	31	/* maximum name segment length we bother with */
95
96struct pshmobj {
97	void *			pshmo_memobject;
98	memory_object_size_t	pshmo_size;
99	struct pshmobj *	pshmo_next;
100};
101
102struct pshminfo {
103	unsigned int	pshm_flags;
104	unsigned int	pshm_usecount;
105	off_t		pshm_length;
106	mode_t		pshm_mode;
107	uid_t		pshm_uid;
108	gid_t		pshm_gid;
109	char		pshm_name[PSHMNAMLEN + 1];	/* segment name */
110	struct pshmobj *pshm_memobjects;
111#if DIAGNOSTIC
112	unsigned int 	pshm_readcount;
113	unsigned int 	pshm_writecount;
114	proc_t		pshm_proc;
115#endif /* DIAGNOSTIC */
116	struct label*	pshm_label;
117};
118#define PSHMINFO_NULL (struct pshminfo *)0
119
120#define	PSHM_NONE	0x001
121#define	PSHM_DEFINED	0x002
122#define	PSHM_ALLOCATED	0x004
123#define	PSHM_MAPPED	0x008
124#define	PSHM_INUSE	0x010
125#define	PSHM_REMOVED	0x020
126#define	PSHM_INCREATE	0x040
127#define	PSHM_INDELETE	0x080
128#define PSHM_ALLOCATING	0x100
129
130struct	pshmcache {
131	LIST_ENTRY(pshmcache) pshm_hash;	/* hash chain */
132	struct	pshminfo *pshminfo;		/* vnode the name refers to */
133	int	pshm_nlen;		/* length of name */
134	char	pshm_name[PSHMNAMLEN + 1];	/* segment name */
135};
136#define PSHMCACHE_NULL (struct pshmcache *)0
137
138struct	pshmstats {
139	long	goodhits;		/* hits that we can really use */
140	long	neghits;		/* negative hits that we can use */
141	long	badhits;		/* hits we must drop */
142	long	falsehits;		/* hits with id mismatch */
143	long	miss;		/* misses */
144	long	longnames;		/* long names that ignore cache */
145};
146
147struct pshmname {
148	char	*pshm_nameptr;	/* pointer to looked up name */
149	long	pshm_namelen;	/* length of looked up component */
150	u_long	pshm_hash;	/* hash value of looked up name */
151};
152
153struct pshmnode {
154	off_t  		mapp_addr;
155	user_size_t	map_size;	/* XXX unused ? */
156	struct pshminfo *pinfo;
157	unsigned int	pshm_usecount;
158#if DIAGNOSTIC
159	unsigned int readcnt;
160	unsigned int writecnt;
161#endif
162};
163#define PSHMNODE_NULL (struct pshmnode *)0
164
165
166#define PSHMHASH(pnp) \
167	(&pshmhashtbl[(pnp)->pshm_hash & pshmhash])
168
169LIST_HEAD(pshmhashhead, pshmcache) *pshmhashtbl;	/* Hash Table */
170u_long	pshmhash;				/* size of hash table - 1 */
171long	pshmnument;			/* number of cache entries allocated */
172struct pshmstats pshmstats;		/* cache effectiveness statistics */
173
174static int pshm_read (struct fileproc *fp, struct uio *uio,
175		    int flags, vfs_context_t ctx);
176static int pshm_write (struct fileproc *fp, struct uio *uio,
177		    int flags, vfs_context_t ctx);
178static int pshm_ioctl (struct fileproc *fp, u_long com,
179		    caddr_t data, vfs_context_t ctx);
180static int pshm_select (struct fileproc *fp, int which, void *wql, vfs_context_t ctx);
181static int pshm_close(struct pshminfo *pinfo, int dropref);
182static int pshm_closefile (struct fileglob *fg, vfs_context_t ctx);
183
184static int pshm_kqfilter(struct fileproc *fp, struct knote *kn, vfs_context_t ctx);
185
186int pshm_access(struct pshminfo *pinfo, int mode, kauth_cred_t cred, proc_t p);
187static int pshm_cache_add(struct pshminfo *pshmp, struct pshmname *pnp, struct pshmcache *pcp);
188static void pshm_cache_delete(struct pshmcache *pcp);
189#if NOT_USED
190static void pshm_cache_purge(void);
191#endif	/* NOT_USED */
192static int pshm_cache_search(struct pshminfo **pshmp, struct pshmname *pnp,
193	struct pshmcache **pcache, int addref);
194
195static const struct fileops pshmops = {
196	DTYPE_PSXSHM,
197	pshm_read,
198	pshm_write,
199	pshm_ioctl,
200	pshm_select,
201	pshm_closefile,
202	pshm_kqfilter,
203	0
204};
205
206static lck_grp_t       *psx_shm_subsys_lck_grp;
207static lck_grp_attr_t  *psx_shm_subsys_lck_grp_attr;
208static lck_attr_t      *psx_shm_subsys_lck_attr;
209static lck_mtx_t        psx_shm_subsys_mutex;
210
211#define PSHM_SUBSYS_LOCK() lck_mtx_lock(& psx_shm_subsys_mutex)
212#define PSHM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_shm_subsys_mutex)
213
214
215/* Initialize the mutex governing access to the posix shm subsystem */
216__private_extern__ void
217pshm_lock_init( void )
218{
219
220    psx_shm_subsys_lck_grp_attr = lck_grp_attr_alloc_init();
221
222    psx_shm_subsys_lck_grp = lck_grp_alloc_init("posix shared memory", psx_shm_subsys_lck_grp_attr);
223
224    psx_shm_subsys_lck_attr = lck_attr_alloc_init();
225    lck_mtx_init(& psx_shm_subsys_mutex, psx_shm_subsys_lck_grp, psx_shm_subsys_lck_attr);
226}
227
228/*
229 * Lookup an entry in the cache
230 *
231 *
232 * status of -1 is returned if matches
233 * If the lookup determines that the name does not exist
234 * (negative cacheing), a status of ENOENT is returned. If the lookup
235 * fails, a status of zero is returned.
236 */
237
238static int
239pshm_cache_search(struct pshminfo **pshmp, struct pshmname *pnp,
240	struct pshmcache **pcache, int addref)
241{
242	struct pshmcache *pcp, *nnp;
243	struct pshmhashhead *pcpp;
244
245	if (pnp->pshm_namelen > PSHMNAMLEN) {
246		pshmstats.longnames++;
247		return (0);
248	}
249
250	pcpp = PSHMHASH(pnp);
251	for (pcp = pcpp->lh_first; pcp != 0; pcp = nnp) {
252		nnp = pcp->pshm_hash.le_next;
253		if (pcp->pshm_nlen == pnp->pshm_namelen &&
254		    !bcmp(pcp->pshm_name, pnp->pshm_nameptr, 						(u_int)pcp-> pshm_nlen))
255			break;
256	}
257
258	if (pcp == 0) {
259		pshmstats.miss++;
260		return (0);
261	}
262
263	/* We found a "positive" match, return the vnode */
264        if (pcp->pshminfo) {
265		pshmstats.goodhits++;
266		/* TOUCH(ncp); */
267		*pshmp = pcp->pshminfo;
268		*pcache = pcp;
269		if (addref)
270			pcp->pshminfo->pshm_usecount++;
271		return (-1);
272	}
273
274	/*
275	 * We found a "negative" match, ENOENT notifies client of this match.
276	 * The nc_vpid field records whether this is a whiteout.
277	 */
278	pshmstats.neghits++;
279	return (ENOENT);
280}
281
282/*
283 * Add an entry to the cache.
284 * XXX should be static?
285 */
286static int
287pshm_cache_add(struct pshminfo *pshmp, struct pshmname *pnp, struct pshmcache *pcp)
288{
289	struct pshmhashhead *pcpp;
290	struct pshminfo *dpinfo;
291	struct pshmcache *dpcp;
292
293#if DIAGNOSTIC
294	if (pnp->pshm_namelen > PSHMNAMLEN)
295		panic("cache_enter: name too long");
296#endif
297
298
299	/*  if the entry has already been added by some one else return */
300	if (pshm_cache_search(&dpinfo, pnp, &dpcp, 0) == -1) {
301		return(EEXIST);
302	}
303	pshmnument++;
304
305	/*
306	 * Fill in cache info, if vp is NULL this is a "negative" cache entry.
307	 * For negative entries, we have to record whether it is a whiteout.
308	 * the whiteout flag is stored in the nc_vpid field which is
309	 * otherwise unused.
310	 */
311	pcp->pshminfo = pshmp;
312	pcp->pshm_nlen = pnp->pshm_namelen;
313	bcopy(pnp->pshm_nameptr, pcp->pshm_name, (unsigned)pcp->pshm_nlen);
314	pcpp = PSHMHASH(pnp);
315#if DIAGNOSTIC
316	{
317		struct pshmcache *p;
318
319		for (p = pcpp->lh_first; p != 0; p = p->pshm_hash.le_next)
320			if (p == pcp)
321				panic("cache_enter: duplicate");
322	}
323#endif
324	LIST_INSERT_HEAD(pcpp, pcp, pshm_hash);
325	return(0);
326}
327
328/*
329 * Name cache initialization, from vfs_init() when we are booting
330 */
331void
332pshm_cache_init(void)
333{
334	pshmhashtbl = hashinit(desiredvnodes / 8, M_SHM, &pshmhash);
335}
336
337#if NOT_USED
338/*
339 * Invalidate a all entries to particular vnode.
340 *
341 * We actually just increment the v_id, that will do it. The entries will
342 * be purged by lookup as they get found. If the v_id wraps around, we
343 * need to ditch the entire cache, to avoid confusion. No valid vnode will
344 * ever have (v_id == 0).
345 */
346static void
347pshm_cache_purge(void)
348{
349	struct pshmcache *pcp;
350	struct pshmhashhead *pcpp;
351
352	for (pcpp = &pshmhashtbl[pshmhash]; pcpp >= pshmhashtbl; pcpp--) {
353		while ( (pcp = pcpp->lh_first) )
354			pshm_cache_delete(pcp);
355	}
356}
357#endif	/* NOT_USED */
358
359static void
360pshm_cache_delete(struct pshmcache *pcp)
361{
362#if DIAGNOSTIC
363	if (pcp->pshm_hash.le_prev == 0)
364		panic("namecache purge le_prev");
365	if (pcp->pshm_hash.le_next == pcp)
366		panic("namecache purge le_next");
367#endif /* DIAGNOSTIC */
368	LIST_REMOVE(pcp, pshm_hash);
369	pcp->pshm_hash.le_prev = 0;
370	pshmnument--;
371}
372
373
374int
375shm_open(proc_t p, struct shm_open_args *uap, int32_t *retval)
376{
377	size_t  i;
378	int indx, error;
379	struct pshmname nd;
380	struct pshminfo *pinfo;
381	struct fileproc *fp = NULL;
382	char *pnbuf = NULL;
383	struct pshminfo *new_pinfo = PSHMINFO_NULL;
384	struct pshmnode *new_pnode = PSHMNODE_NULL;
385	struct pshmcache *pcache = PSHMCACHE_NULL;	/* ignored on return */
386	char * nameptr;
387	char * cp;
388	size_t pathlen, plen;
389	int fmode ;
390	int cmode = uap->mode;
391	int incache = 0;
392	struct pshmcache *pcp = NULL;
393
394	AUDIT_ARG(fflags, uap->oflag);
395	AUDIT_ARG(mode, uap->mode);
396
397	pinfo = PSHMINFO_NULL;
398
399	/*
400	 * Preallocate everything we might need up front to avoid taking
401	 * and dropping the lock, opening us up to race conditions.
402	 */
403	MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
404	if (pnbuf == NULL) {
405		error = ENOSPC;
406		goto bad;
407	}
408
409	pathlen = MAXPATHLEN;
410	error = copyinstr(uap->name, (void *)pnbuf, MAXPATHLEN, &pathlen);
411	if (error) {
412		goto bad;
413	}
414	AUDIT_ARG(text, pnbuf);
415	if (pathlen > PSHMNAMLEN) {
416		error = ENAMETOOLONG;
417		goto bad;
418	}
419#ifdef PSXSHM_NAME_RESTRICT
420	nameptr = pnbuf;
421	if (*nameptr == '/') {
422		while (*(nameptr++) == '/') {
423			plen--;
424			error = EINVAL;
425			goto bad;
426		}
427	} else {
428		error = EINVAL;
429		goto bad;
430	}
431#endif /* PSXSHM_NAME_RESTRICT */
432
433	plen = pathlen;
434	nameptr = pnbuf;
435	nd.pshm_nameptr = nameptr;
436	nd.pshm_namelen = plen;
437	nd. pshm_hash =0;
438
439	for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) {
440		nd.pshm_hash += (unsigned char)*cp * i;
441	}
442
443	/*
444	 * attempt to allocate a new fp; if unsuccessful, the fp will be
445	 * left unmodified (NULL).
446	 */
447	error = falloc(p, &fp, &indx, vfs_context_current());
448	if (error)
449		goto bad;
450
451	cmode &=  ALLPERMS;
452
453	fmode = FFLAGS(uap->oflag);
454	if ((fmode & (FREAD | FWRITE)) == 0) {
455		error = EINVAL;
456		goto bad;
457	}
458
459	/*
460	 * We allocate a new entry if we are less than the maximum
461	 * allowed and the one at the front of the LRU list is in use.
462	 * Otherwise we use the one at the front of the LRU list.
463	 */
464	MALLOC(pcp, struct pshmcache *, sizeof(struct pshmcache), M_SHM, M_WAITOK|M_ZERO);
465	if (pcp == NULL) {
466		error = ENOSPC;
467		goto bad;
468	}
469
470	MALLOC(new_pinfo, struct pshminfo *, sizeof(struct pshminfo), M_SHM, M_WAITOK|M_ZERO);
471	if (new_pinfo == PSHMINFO_NULL) {
472		error = ENOSPC;
473		goto bad;
474	}
475#if CONFIG_MACF
476	mac_posixshm_label_init(new_pinfo);
477#endif
478
479	MALLOC(new_pnode, struct pshmnode *, sizeof(struct pshmnode), M_SHM, M_WAITOK|M_ZERO);
480	if (new_pnode == PSHMNODE_NULL) {
481		error = ENOSPC;
482		goto bad;
483	}
484
485	PSHM_SUBSYS_LOCK();
486
487	/*
488	 * If we find the entry in the cache, this will take a reference,
489	 * allowing us to unlock it for the permissions check.
490	 */
491	error = pshm_cache_search(&pinfo, &nd, &pcache, 1);
492
493	PSHM_SUBSYS_UNLOCK();
494
495	if (error == ENOENT) {
496		error = EINVAL;
497		goto bad;
498	}
499
500	if (!error) {
501		incache = 0;
502		if (fmode & O_CREAT) {
503			/*  create a new one (commit the allocation) */
504			pinfo = new_pinfo;
505			pinfo->pshm_flags = PSHM_DEFINED | PSHM_INCREATE;
506			pinfo->pshm_usecount = 1; /* existence reference */
507			pinfo->pshm_mode = cmode;
508			pinfo->pshm_uid = kauth_getuid();
509			pinfo->pshm_gid = kauth_getgid();
510			bcopy(pnbuf, &pinfo->pshm_name[0], pathlen);
511			pinfo->pshm_name[pathlen]=0;
512#if CONFIG_MACF
513			error = mac_posixshm_check_create(kauth_cred_get(), nameptr);
514			if (error) {
515				goto bad;
516			}
517			mac_posixshm_label_associate(kauth_cred_get(), pinfo, nameptr);
518#endif
519		}
520	} else {
521		incache = 1;
522		if (fmode & O_CREAT) {
523			/*  already exists */
524			if ((fmode & O_EXCL)) {
525				AUDIT_ARG(posix_ipc_perm, pinfo->pshm_uid,
526						pinfo->pshm_gid,
527						pinfo->pshm_mode);
528
529				/* shm obj exists and opened O_EXCL */
530				error = EEXIST;
531				goto bad;
532			}
533
534			if( pinfo->pshm_flags & PSHM_INDELETE) {
535				error = ENOENT;
536				goto bad;
537			}
538			AUDIT_ARG(posix_ipc_perm, pinfo->pshm_uid,
539					pinfo->pshm_gid, pinfo->pshm_mode);
540#if CONFIG_MACF
541			if ((error = mac_posixshm_check_open(kauth_cred_get(), pinfo, fmode))) {
542				goto bad;
543			}
544#endif
545			if ( (error = pshm_access(pinfo, fmode, kauth_cred_get(), p)) ) {
546				goto bad;
547			}
548		}
549	}
550	if (!(fmode & O_CREAT)) {
551		if (!incache) {
552			/* O_CREAT is not set and the object does not exist */
553			error = ENOENT;
554			goto bad;
555		}
556		if( pinfo->pshm_flags & PSHM_INDELETE) {
557			error = ENOENT;
558			goto bad;
559		}
560#if CONFIG_MACF
561		if ((error = mac_posixshm_check_open(kauth_cred_get(), pinfo, fmode))) {
562			goto bad;
563		}
564#endif
565
566		if ((error = pshm_access(pinfo, fmode, kauth_cred_get(), p))) {
567			goto bad;
568		}
569	}
570	if (fmode & O_TRUNC) {
571		error = EINVAL;
572		goto bad;
573	}
574
575
576	PSHM_SUBSYS_LOCK();
577
578#if DIAGNOSTIC
579	if (fmode & FWRITE)
580		pinfo->pshm_writecount++;
581	if (fmode & FREAD)
582		pinfo->pshm_readcount++;
583#endif
584	if (!incache) {
585		/* if successful, this will consume the pcp */
586		if ( (error = pshm_cache_add(pinfo, &nd, pcp)) ) {
587			goto bad_locked;
588		}
589		/*
590		 * add reference for the new entry; otherwise, we obtained
591		 * one from the cache hit earlier.
592		 */
593		pinfo->pshm_usecount++;
594	}
595	pinfo->pshm_flags &= ~PSHM_INCREATE;
596	new_pnode->pinfo = pinfo;
597
598	PSHM_SUBSYS_UNLOCK();
599
600	/*
601	 * if incache, we did not use the new pcp or new_pinfo and must
602	 * free them
603	 */
604	if (incache) {
605		FREE(pcp, M_SHM);
606
607		if (new_pinfo != PSHMINFO_NULL) {
608#if CONFIG_MACF
609			mac_posixshm_label_destroy(new_pinfo);
610#endif
611			FREE(new_pinfo, M_SHM);
612		}
613	}
614
615	proc_fdlock(p);
616	fp->f_flag = fmode & FMASK;
617	fp->f_ops = &pshmops;
618	fp->f_data = (caddr_t)new_pnode;
619	*fdflags(p, indx) |= UF_EXCLOSE;
620	procfdtbl_releasefd(p, indx, NULL);
621	fp_drop(p, indx, fp, 1);
622	proc_fdunlock(p);
623
624	*retval = indx;
625	FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
626	return (0);
627
628bad_locked:
629	PSHM_SUBSYS_UNLOCK();
630bad:
631	/*
632	 * If we obtained the entry from the cache, we need to drop the
633	 * reference; holding the reference may have prevented unlinking,
634	 * so we need to call pshm_close() to get the full effect.
635	 */
636	if (incache) {
637		PSHM_SUBSYS_LOCK();
638		pshm_close(pinfo, 1);
639		PSHM_SUBSYS_UNLOCK();
640	}
641
642	if (pcp != NULL)
643		FREE(pcp, M_SHM);
644
645	if (new_pnode != PSHMNODE_NULL)
646		FREE(new_pnode, M_SHM);
647
648	if (fp != NULL)
649		fp_free(p, indx, fp);
650
651	if (new_pinfo != PSHMINFO_NULL) {
652#if CONFIG_MACF
653		mac_posixshm_label_destroy(new_pinfo);
654#endif
655		FREE(new_pinfo, M_SHM);
656	}
657	if (pnbuf != NULL)
658		FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
659	return (error);
660}
661
662
663int
664pshm_truncate(__unused proc_t p, struct fileproc *fp, __unused int fd,
665				off_t length, __unused int32_t *retval)
666{
667	struct pshminfo * pinfo;
668	struct pshmnode * pnode ;
669	kern_return_t kret;
670	mem_entry_name_port_t mem_object;
671	mach_vm_size_t total_size, alloc_size;
672	memory_object_size_t mosize;
673	struct pshmobj *pshmobj, *pshmobj_next, **pshmobj_next_p;
674	vm_map_t	user_map;
675#if CONFIG_MACF
676	int error;
677#endif
678
679	user_map = current_map();
680
681	if (fp->f_type != DTYPE_PSXSHM) {
682		return(EINVAL);
683	}
684
685
686	if (((pnode = (struct pshmnode *)fp->f_data)) == PSHMNODE_NULL )
687		return(EINVAL);
688
689	PSHM_SUBSYS_LOCK();
690	if ((pinfo = pnode->pinfo) == PSHMINFO_NULL) {
691		PSHM_SUBSYS_UNLOCK();
692		return(EINVAL);
693	}
694	if ((pinfo->pshm_flags & (PSHM_DEFINED|PSHM_ALLOCATING|PSHM_ALLOCATED))
695			!= PSHM_DEFINED) {
696		PSHM_SUBSYS_UNLOCK();
697		return(EINVAL);
698	}
699#if CONFIG_MACF
700	error = mac_posixshm_check_truncate(kauth_cred_get(), pinfo, length);
701	if (error) {
702		PSHM_SUBSYS_UNLOCK();
703		return(error);
704	}
705#endif
706
707	pinfo->pshm_flags |= PSHM_ALLOCATING;
708	total_size = vm_map_round_page(length,
709				       vm_map_page_mask(user_map));
710	pshmobj_next_p = &pinfo->pshm_memobjects;
711
712	for (alloc_size = 0;
713	     alloc_size < total_size;
714	     alloc_size += mosize) {
715
716		PSHM_SUBSYS_UNLOCK();
717
718		mosize = MIN(total_size - alloc_size, ANON_MAX_SIZE);
719		kret = mach_make_memory_entry_64(
720			VM_MAP_NULL,
721			&mosize,
722			0,
723			MAP_MEM_NAMED_CREATE | VM_PROT_DEFAULT,
724			&mem_object,
725			0);
726
727		if (kret != KERN_SUCCESS)
728			goto out;
729
730		MALLOC(pshmobj, struct pshmobj *, sizeof (struct pshmobj),
731		       M_SHM, M_WAITOK);
732		if (pshmobj == NULL) {
733			kret = KERN_NO_SPACE;
734			mach_memory_entry_port_release(mem_object);
735			mem_object = NULL;
736			goto out;
737		}
738
739		PSHM_SUBSYS_LOCK();
740
741		pshmobj->pshmo_memobject = (void *) mem_object;
742		pshmobj->pshmo_size = mosize;
743		pshmobj->pshmo_next = NULL;
744
745		*pshmobj_next_p = pshmobj;
746		pshmobj_next_p = &pshmobj->pshmo_next;
747	}
748
749	pinfo->pshm_flags = PSHM_ALLOCATED;
750	pinfo->pshm_length = total_size;
751	PSHM_SUBSYS_UNLOCK();
752	return(0);
753
754out:
755	PSHM_SUBSYS_LOCK();
756	for (pshmobj = pinfo->pshm_memobjects;
757	     pshmobj != NULL;
758	     pshmobj = pshmobj_next) {
759		pshmobj_next = pshmobj->pshmo_next;
760		mach_memory_entry_port_release(pshmobj->pshmo_memobject);
761		FREE(pshmobj, M_SHM);
762	}
763	pinfo->pshm_memobjects = NULL;
764	pinfo->pshm_flags &= ~PSHM_ALLOCATING;
765	PSHM_SUBSYS_UNLOCK();
766
767	switch (kret) {
768	case KERN_INVALID_ADDRESS:
769	case KERN_NO_SPACE:
770		return (ENOMEM);
771	case KERN_PROTECTION_FAILURE:
772		return (EACCES);
773	default:
774		return (EINVAL);
775
776	}
777}
778
779int
780pshm_stat(struct pshmnode *pnode, void *ub, int isstat64)
781{
782	struct stat *sb = (struct stat *)0;	/* warning avoidance ; protected by isstat64 */
783	struct stat64 * sb64 = (struct stat64 *)0;  /* warning avoidance ; protected by isstat64 */
784	struct pshminfo *pinfo;
785#if CONFIG_MACF
786	int error;
787#endif
788
789	PSHM_SUBSYS_LOCK();
790	if ((pinfo = pnode->pinfo) == PSHMINFO_NULL){
791		PSHM_SUBSYS_UNLOCK();
792		return(EINVAL);
793	}
794
795#if CONFIG_MACF
796	error = mac_posixshm_check_stat(kauth_cred_get(), pinfo);
797	if (error) {
798		PSHM_SUBSYS_UNLOCK();
799		return(error);
800	}
801#endif
802
803	if (isstat64 != 0) {
804		sb64 = (struct stat64 *)ub;
805		bzero(sb64, sizeof(struct stat64));
806		sb64->st_mode = pinfo->pshm_mode;
807		sb64->st_uid = pinfo->pshm_uid;
808		sb64->st_gid = pinfo->pshm_gid;
809		sb64->st_size = pinfo->pshm_length;
810	} else {
811		sb = (struct stat *)ub;
812		bzero(sb, sizeof(struct stat));
813		sb->st_mode = pinfo->pshm_mode;
814		sb->st_uid = pinfo->pshm_uid;
815		sb->st_gid = pinfo->pshm_gid;
816		sb->st_size = pinfo->pshm_length;
817	}
818	PSHM_SUBSYS_UNLOCK();
819
820	return(0);
821}
822
823/*
824 * This is called only from shm_open which holds pshm_lock();
825 * XXX This code is repeated many times
826 */
827int
828pshm_access(struct pshminfo *pinfo, int mode, kauth_cred_t cred, __unused proc_t p)
829{
830	int mode_req = ((mode & FREAD) ? S_IRUSR : 0) |
831		       ((mode & FWRITE) ? S_IWUSR : 0);
832
833	/* Otherwise, user id 0 always gets access. */
834	if (!suser(cred, NULL))
835		return (0);
836
837	return(posix_cred_access(cred, pinfo->pshm_uid, pinfo->pshm_gid, pinfo->pshm_mode, mode_req));
838}
839
840int
841pshm_mmap(__unused proc_t p, struct mmap_args *uap, user_addr_t *retval, struct fileproc *fp, off_t pageoff)
842{
843	vm_map_offset_t	user_addr = (vm_map_offset_t)uap->addr;
844	vm_map_size_t	user_size = (vm_map_size_t)uap->len ;
845	vm_map_offset_t	user_start_addr;
846	vm_map_size_t	map_size, mapped_size;
847	int prot = uap->prot;
848	int flags = uap->flags;
849	vm_object_offset_t file_pos = (vm_object_offset_t)uap->pos;
850	vm_object_offset_t map_pos;
851	vm_map_t	user_map;
852	int		alloc_flags;
853	boolean_t 	docow;
854	kern_return_t	kret;
855	struct pshminfo * pinfo;
856	struct pshmnode * pnode;
857	struct pshmobj * pshmobj;
858#if CONFIG_MACF
859	int error;
860#endif
861
862	if (user_size == 0)
863		return(0);
864
865	if ((flags & MAP_SHARED) == 0)
866		return(EINVAL);
867
868
869	if ((prot & PROT_WRITE) && ((fp->f_flag & FWRITE) == 0)) {
870		return(EPERM);
871	}
872
873	if (((pnode = (struct pshmnode *)fp->f_data)) == PSHMNODE_NULL )
874		return(EINVAL);
875
876	PSHM_SUBSYS_LOCK();
877	if ((pinfo = pnode->pinfo) == PSHMINFO_NULL) {
878		PSHM_SUBSYS_UNLOCK();
879		return(EINVAL);
880	}
881
882	if ((pinfo->pshm_flags & PSHM_ALLOCATED) != PSHM_ALLOCATED) {
883		PSHM_SUBSYS_UNLOCK();
884		return(EINVAL);
885	}
886	if ((off_t)user_size > pinfo->pshm_length) {
887		PSHM_SUBSYS_UNLOCK();
888		return(EINVAL);
889	}
890	if ((off_t)(user_size + file_pos) > pinfo->pshm_length) {
891		PSHM_SUBSYS_UNLOCK();
892		return(EINVAL);
893	}
894	if ((pshmobj = pinfo->pshm_memobjects) == NULL) {
895		PSHM_SUBSYS_UNLOCK();
896		return(EINVAL);
897	}
898
899#if CONFIG_MACF
900	error = mac_posixshm_check_mmap(kauth_cred_get(), pinfo, prot, flags);
901	if (error) {
902		PSHM_SUBSYS_UNLOCK();
903		return(error);
904	}
905#endif
906
907	PSHM_SUBSYS_UNLOCK();
908	user_map = current_map();
909
910	if ((flags & MAP_FIXED) == 0) {
911		alloc_flags = VM_FLAGS_ANYWHERE;
912		user_addr = vm_map_round_page(user_addr,
913					      vm_map_page_mask(user_map));
914	} else {
915		if (user_addr != vm_map_round_page(user_addr,
916						   vm_map_page_mask(user_map)))
917			return (EINVAL);
918		/*
919		 * We do not get rid of the existing mappings here because
920		 * it wouldn't be atomic (see comment in mmap()).  We let
921		 * Mach VM know that we want it to replace any existing
922		 * mapping with the new one.
923		 */
924		alloc_flags = VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE;
925	}
926	docow = FALSE;
927
928	mapped_size = 0;
929
930	/* reserver the entire space first... */
931	kret = vm_map_enter_mem_object(user_map,
932				       &user_addr,
933				       user_size,
934				       0,
935				       alloc_flags,
936				       IPC_PORT_NULL,
937				       0,
938				       FALSE,
939				       VM_PROT_NONE,
940				       VM_PROT_NONE,
941				       VM_INHERIT_NONE);
942	user_start_addr = user_addr;
943	if (kret != KERN_SUCCESS) {
944		goto out;
945	}
946
947	/* ... and overwrite with the real mappings */
948	for (map_pos = 0, pshmobj = pinfo->pshm_memobjects;
949	     user_size != 0;
950	     map_pos += pshmobj->pshmo_size, pshmobj = pshmobj->pshmo_next) {
951		if (pshmobj == NULL) {
952			/* nothing there to map !? */
953			goto out;
954		}
955		if (file_pos >= map_pos + pshmobj->pshmo_size) {
956			continue;
957		}
958		map_size = pshmobj->pshmo_size - (file_pos - map_pos);
959		if (map_size > user_size) {
960			map_size = user_size;
961		}
962		kret = vm_map_enter_mem_object(
963			user_map,
964			&user_addr,
965			map_size,
966			0,
967			VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
968			pshmobj->pshmo_memobject,
969			file_pos - map_pos,
970			docow,
971			prot,
972			VM_PROT_DEFAULT,
973			VM_INHERIT_SHARE);
974		if (kret != KERN_SUCCESS)
975			goto out;
976
977		user_addr += map_size;
978		user_size -= map_size;
979		mapped_size += map_size;
980		file_pos += map_size;
981	}
982
983	PSHM_SUBSYS_LOCK();
984	pnode->mapp_addr = user_start_addr;
985	pnode->map_size = mapped_size;
986	pinfo->pshm_flags |= (PSHM_MAPPED | PSHM_INUSE);
987	PSHM_SUBSYS_UNLOCK();
988out:
989	if (kret != KERN_SUCCESS) {
990		if (mapped_size != 0) {
991			(void) mach_vm_deallocate(current_map(),
992						  user_start_addr,
993						  mapped_size);
994		}
995	}
996
997	switch (kret) {
998	case KERN_SUCCESS:
999		*retval = (user_start_addr + pageoff);
1000		return (0);
1001	case KERN_INVALID_ADDRESS:
1002	case KERN_NO_SPACE:
1003		return (ENOMEM);
1004	case KERN_PROTECTION_FAILURE:
1005		return (EACCES);
1006	default:
1007		return (EINVAL);
1008	}
1009
1010}
1011
1012int
1013shm_unlink(__unused proc_t p, struct shm_unlink_args *uap,
1014			__unused int32_t *retval)
1015{
1016	size_t i;
1017	int error=0;
1018	struct pshmname nd;
1019	struct pshminfo *pinfo;
1020	char * pnbuf;
1021	char * nameptr;
1022	char * cp;
1023	size_t pathlen, plen;
1024	int incache = 0;
1025	struct pshmcache *pcache = PSHMCACHE_NULL;
1026	struct pshmobj *pshmobj, *pshmobj_next;
1027
1028	pinfo = PSHMINFO_NULL;
1029
1030	MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
1031	if (pnbuf == NULL) {
1032		return(ENOSPC);		/* XXX non-standard */
1033	}
1034	pathlen = MAXPATHLEN;
1035	error = copyinstr(uap->name, (void *)pnbuf, MAXPATHLEN, &pathlen);
1036	if (error) {
1037		goto bad;
1038	}
1039	AUDIT_ARG(text, pnbuf);
1040	if (pathlen > PSHMNAMLEN) {
1041		error = ENAMETOOLONG;
1042		goto bad;
1043	}
1044
1045
1046#ifdef PSXSHM_NAME_RESTRICT
1047	nameptr = pnbuf;
1048	if (*nameptr == '/') {
1049		while (*(nameptr++) == '/') {
1050			plen--;
1051			error = EINVAL;
1052			goto bad;
1053		}
1054        } else {
1055		error = EINVAL;
1056		goto bad;
1057	}
1058#endif /* PSXSHM_NAME_RESTRICT */
1059
1060	plen = pathlen;
1061	nameptr = pnbuf;
1062	nd.pshm_nameptr = nameptr;
1063	nd.pshm_namelen = plen;
1064	nd. pshm_hash =0;
1065
1066        for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) {
1067               nd.pshm_hash += (unsigned char)*cp * i;
1068	}
1069
1070	PSHM_SUBSYS_LOCK();
1071	error = pshm_cache_search(&pinfo, &nd, &pcache, 0);
1072
1073	if (error == ENOENT) {
1074		PSHM_SUBSYS_UNLOCK();
1075		goto bad;
1076
1077	}
1078	/* During unlink lookup failure also implies ENOENT */
1079	if (!error) {
1080		PSHM_SUBSYS_UNLOCK();
1081		error = ENOENT;
1082		goto bad;
1083	} else
1084		incache = 1;
1085
1086	if ((pinfo->pshm_flags & (PSHM_DEFINED | PSHM_ALLOCATED))==0) {
1087		PSHM_SUBSYS_UNLOCK();
1088		error = EINVAL;
1089		goto bad;
1090	}
1091
1092	if (pinfo->pshm_flags & PSHM_ALLOCATING) {
1093		/* XXX should we wait for flag to clear and then proceed ? */
1094		PSHM_SUBSYS_UNLOCK();
1095		error = EAGAIN;
1096		goto bad;
1097	}
1098
1099	if (pinfo->pshm_flags & PSHM_INDELETE) {
1100		PSHM_SUBSYS_UNLOCK();
1101		error = 0;
1102		goto bad;
1103	}
1104#if CONFIG_MACF
1105	error = mac_posixshm_check_unlink(kauth_cred_get(), pinfo, nameptr);
1106	if (error) {
1107		PSHM_SUBSYS_UNLOCK();
1108		goto bad;
1109	}
1110#endif
1111
1112	AUDIT_ARG(posix_ipc_perm, pinfo->pshm_uid, pinfo->pshm_gid,
1113		  pinfo->pshm_mode);
1114
1115	/*
1116	 * following file semantics, unlink should be allowed
1117	 * for users with write permission only.
1118	 */
1119	if ( (error = pshm_access(pinfo, FWRITE, kauth_cred_get(), p)) ) {
1120		PSHM_SUBSYS_UNLOCK();
1121		goto bad;
1122	}
1123
1124	pinfo->pshm_flags |= PSHM_INDELETE;
1125	pshm_cache_delete(pcache);
1126	pinfo->pshm_flags |= PSHM_REMOVED;
1127	/* release the existence reference */
1128 	if (!--pinfo->pshm_usecount) {
1129#if CONFIG_MACF
1130		mac_posixshm_label_destroy(pinfo);
1131#endif
1132		PSHM_SUBSYS_UNLOCK();
1133		/*
1134		 * If this is the last reference going away on the object,
1135		 * then we need to destroy the backing object.  The name
1136		 * has an implied but uncounted reference on the object,
1137		 * once it's created, since it's used as a rendezvous, and
1138		 * therefore may be subsequently reopened.
1139		 */
1140		for (pshmobj = pinfo->pshm_memobjects;
1141		     pshmobj != NULL;
1142		     pshmobj = pshmobj_next) {
1143			mach_memory_entry_port_release(pshmobj->pshmo_memobject);
1144			pshmobj_next = pshmobj->pshmo_next;
1145			FREE(pshmobj, M_SHM);
1146		}
1147		FREE(pinfo,M_SHM);
1148	} else {
1149		PSHM_SUBSYS_UNLOCK();
1150	}
1151	FREE(pcache, M_SHM);
1152	error = 0;
1153bad:
1154	FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
1155	return (error);
1156}
1157
1158/* already called locked */
1159static int
1160pshm_close(struct pshminfo *pinfo, int dropref)
1161{
1162	int error = 0;
1163	struct pshmobj *pshmobj, *pshmobj_next;
1164
1165	/*
1166	 * If we are dropping the reference we took on the cache object, don't
1167	 * enforce the allocation requirement.
1168	 */
1169	if ( !dropref && ((pinfo->pshm_flags & PSHM_ALLOCATED) != PSHM_ALLOCATED)) {
1170		return(EINVAL);
1171	}
1172#if DIAGNOSTIC
1173	if(!pinfo->pshm_usecount) {
1174		kprintf("negative usecount in pshm_close\n");
1175	}
1176#endif /* DIAGNOSTIC */
1177	pinfo->pshm_usecount--; /* release this fd's reference */
1178
1179 	if ((pinfo->pshm_flags & PSHM_REMOVED) && !pinfo->pshm_usecount) {
1180#if CONFIG_MACF
1181		mac_posixshm_label_destroy(pinfo);
1182#endif
1183		PSHM_SUBSYS_UNLOCK();
1184		/*
1185		 * If this is the last reference going away on the object,
1186		 * then we need to destroy the backing object.
1187		 */
1188		for (pshmobj = pinfo->pshm_memobjects;
1189		     pshmobj != NULL;
1190		     pshmobj = pshmobj_next) {
1191			mach_memory_entry_port_release(pshmobj->pshmo_memobject);
1192			pshmobj_next = pshmobj->pshmo_next;
1193			FREE(pshmobj, M_SHM);
1194		}
1195		PSHM_SUBSYS_LOCK();
1196		FREE(pinfo,M_SHM);
1197	}
1198	return (error);
1199}
1200
1201/* vfs_context_t passed to match prototype for struct fileops */
1202static int
1203pshm_closefile(struct fileglob *fg, __unused vfs_context_t ctx)
1204{
1205	int error = EINVAL;
1206	struct pshmnode *pnode;
1207
1208	PSHM_SUBSYS_LOCK();
1209
1210	if ((pnode = (struct pshmnode *)fg->fg_data) != NULL) {
1211		if (pnode->pinfo != PSHMINFO_NULL) {
1212			error =  pshm_close(pnode->pinfo, 0);
1213		}
1214		FREE(pnode, M_SHM);
1215	}
1216
1217	PSHM_SUBSYS_UNLOCK();
1218
1219	return(error);
1220}
1221
1222static int
1223pshm_read(__unused struct fileproc *fp, __unused struct uio *uio,
1224			__unused int flags, __unused vfs_context_t ctx)
1225{
1226	return(ENOTSUP);
1227}
1228
1229static int
1230pshm_write(__unused struct fileproc *fp, __unused struct uio *uio,
1231			__unused int flags, __unused vfs_context_t ctx)
1232{
1233	return(ENOTSUP);
1234}
1235
1236static int
1237pshm_ioctl(__unused struct fileproc *fp, __unused u_long com,
1238			__unused caddr_t data, __unused vfs_context_t ctx)
1239{
1240	return(ENOTSUP);
1241}
1242
1243static int
1244pshm_select(__unused struct fileproc *fp, __unused int which, __unused void *wql,
1245			__unused vfs_context_t ctx)
1246{
1247	return(ENOTSUP);
1248}
1249
1250static int
1251pshm_kqfilter(__unused struct fileproc *fp, __unused struct knote *kn,
1252				__unused vfs_context_t ctx)
1253{
1254	return(ENOTSUP);
1255}
1256
1257int
1258fill_pshminfo(struct pshmnode * pshm, struct pshm_info * info)
1259{
1260	struct pshminfo *pinfo;
1261	struct vinfo_stat *sb;
1262
1263	PSHM_SUBSYS_LOCK();
1264	if ((pinfo = pshm->pinfo) == PSHMINFO_NULL){
1265		PSHM_SUBSYS_UNLOCK();
1266		return(EINVAL);
1267	}
1268
1269	sb = &info->pshm_stat;
1270
1271	bzero(sb, sizeof(struct vinfo_stat));
1272	sb->vst_mode = pinfo->pshm_mode;
1273	sb->vst_uid = pinfo->pshm_uid;
1274	sb->vst_gid = pinfo->pshm_gid;
1275	sb->vst_size = pinfo->pshm_length;
1276
1277	info->pshm_mappaddr = pshm->mapp_addr;
1278	bcopy(&pinfo->pshm_name[0], &info->pshm_name[0], PSHMNAMLEN+1);
1279
1280	PSHM_SUBSYS_UNLOCK();
1281	return(0);
1282}
1283
1284#if CONFIG_MACF
1285void
1286pshm_label_associate(struct fileproc *fp, struct vnode *vp, vfs_context_t ctx)
1287{
1288	struct pshmnode *pnode;
1289	struct pshminfo *pshm;
1290
1291	PSHM_SUBSYS_LOCK();
1292	pnode = (struct pshmnode *)fp->f_fglob->fg_data;
1293	if (pnode != NULL) {
1294		pshm = pnode->pinfo;
1295		if (pshm != NULL)
1296			mac_posixshm_vnode_label_associate(
1297				vfs_context_ucred(ctx), pshm, pshm->pshm_label,
1298				vp, vp->v_label);
1299	}
1300	PSHM_SUBSYS_UNLOCK();
1301}
1302#endif
1303