xref: /xnu-8796.121.2/bsd/kern/posix_shm.c (revision c54f35ca767986246321eb901baf8f5ff7923f6a)
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 <sys/posix_shm.h>
71 #include <security/audit/audit.h>
72 #include <stdbool.h>
73 
74 #if CONFIG_MACF
75 #include <security/mac_framework.h>
76 #endif
77 
78 #include <mach/mach_types.h>
79 #include <mach/mach_vm.h>
80 #include <mach/vm_map.h>
81 #include <mach/vm_prot.h>
82 #include <mach/vm_inherit.h>
83 #include <mach/kern_return.h>
84 
85 #include <vm/vm_map.h>
86 #include <vm/vm_protos.h>
87 
88 #define f_flag fp_glob->fg_flag
89 #define f_ops fp_glob->fg_ops
90 
91 /*
92  * Used to construct the list of memory objects
93  * assigned to a populated shared memory segment.
94  */
95 typedef struct pshm_mobj {
96 	void                  *pshmo_memobject;
97 	memory_object_size_t  pshmo_size;
98 	SLIST_ENTRY(pshm_mobj) pshmo_next;
99 } pshm_mobj_t;
100 
101 /*
102  * This represents an existing Posix shared memory object.
103  *
104  * It comes into existence with a shm_open(...O_CREAT...)
105  * call and goes away only after it has been shm_unlink()ed
106  * and the last remaining shm_open() file reference is closed.
107  *
108  * To keep track of that lifetime, pshm_usecount is used as a reference
109  * counter. It's incremented for every successful shm_open() and
110  * one extra time for the shm_unlink() to release. Internally
111  * you can temporarily use an additional reference whenever the
112  * subsystem lock has to be dropped for other reasons.
113  */
114 typedef struct internal_pshminfo {
115 	struct pshminfo pshm_hdr;
116 	SLIST_HEAD(pshm_mobjhead, pshm_mobj) pshm_mobjs;
117 	RB_ENTRY(internal_pshminfo) pshm_links;        /* links for red/black tree */
118 } pshm_info_t;
119 #define pshm_flags    pshm_hdr.pshm_flags
120 #define pshm_usecount pshm_hdr.pshm_usecount
121 #define pshm_length   pshm_hdr.pshm_length
122 #define pshm_mode     pshm_hdr.pshm_mode
123 #define pshm_uid      pshm_hdr.pshm_uid
124 #define pshm_gid      pshm_hdr.pshm_gid
125 #define pshm_label    pshm_hdr.pshm_label
126 
127 /* Values for pshm_flags that are still used */
128 #define PSHM_ALLOCATED  0x004   /* backing storage is allocated */
129 #define PSHM_MAPPED     0x008   /* mapped at least once */
130 #define PSHM_INUSE      0x010   /* mapped at least once */
131 #define PSHM_REMOVED    0x020   /* no longer in the name cache due to shm_unlink() */
132 #define PSHM_ALLOCATING 0x100   /* storage is being allocated */
133 
134 /*
135  * These handle reference counting pshm_info_t structs using pshm_usecount.
136  */
137 static int pshm_ref(pshm_info_t *pinfo);
138 static void pshm_deref(pshm_info_t *pinfo);
139 #define PSHM_MAXCOUNT UINT_MAX
140 
141 /*
142  * For every shm_open, we get a new one of these.
143  * The only reason we don't just use pshm_info directly is that
144  * you can query the mapped memory objects via proc_pidinfo to
145  * query the mapped address. Note that even this is a hack. If
146  * you mmap() the same fd multiple times, we only save/report
147  * one address.
148  */
149 typedef struct pshmnode {
150 	off_t       mapp_addr;
151 	pshm_info_t *pinfo;
152 } pshmnode_t;
153 
154 
155 /* compare function for the red black tree */
156 static int
pshm_compare(pshm_info_t * a,pshm_info_t * b)157 pshm_compare(pshm_info_t *a, pshm_info_t *b)
158 {
159 	int cmp = strncmp(a->pshm_hdr.pshm_name, b->pshm_hdr.pshm_name, PSHMNAMLEN + 1);
160 
161 	if (cmp < 0) {
162 		return -1;
163 	}
164 	if (cmp > 0) {
165 		return 1;
166 	}
167 	return 0;
168 }
169 
170 
171 /*
172  * shared memory "paths" are stored in a red black tree for lookup
173  */
174 u_long pshmnument;    /* count of entries allocated in the red black tree */
175 RB_HEAD(pshmhead, internal_pshminfo) pshm_head;
176 RB_PROTOTYPE(pshmhead, internal_pshminfo, pshm_links, pshm_compare)
177 RB_GENERATE(pshmhead, internal_pshminfo, pshm_links, pshm_compare)
178 
179 /* lookup, add, remove functions */
180 static pshm_info_t *pshm_cache_search(pshm_info_t * look);
181 static void pshm_cache_add(pshm_info_t *entry);
182 static void pshm_cache_delete(pshm_info_t *entry);
183 
184 static int pshm_closefile(struct fileglob *fg, vfs_context_t ctx);
185 
186 static int pshm_access(pshm_info_t *pinfo, int mode, kauth_cred_t cred, proc_t p);
187 int pshm_cache_purge_all(void);
188 int pshm_cache_purge_uid(uid_t uid);
189 
190 static void pshm_unlink_internal(pshm_info_t *pinfo);
191 
192 static const struct fileops pshmops = {
193 	.fo_type     = DTYPE_PSXSHM,
194 	.fo_read     = fo_no_read,
195 	.fo_write    = fo_no_write,
196 	.fo_ioctl    = fo_no_ioctl,
197 	.fo_select   = fo_no_select,
198 	.fo_close    = pshm_closefile,
199 	.fo_drain    = fo_no_drain,
200 	.fo_kqfilter = fo_no_kqfilter,
201 };
202 
203 /*
204  * Everything here is protected by a single mutex.
205  */
206 static LCK_GRP_DECLARE(psx_shm_subsys_lck_grp, "posix shared memory");
207 static LCK_MTX_DECLARE(psx_shm_subsys_mutex, &psx_shm_subsys_lck_grp);
208 
209 #define PSHM_SUBSYS_LOCK() lck_mtx_lock(& psx_shm_subsys_mutex)
210 #define PSHM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_shm_subsys_mutex)
211 #define PSHM_SUBSYS_ASSERT_HELD()  LCK_MTX_ASSERT(&psx_shm_subsys_mutex, LCK_MTX_ASSERT_OWNED)
212 
213 /*
214  * Lookup an entry in the cache. Only the name is used from "look".
215  */
216 static pshm_info_t *
pshm_cache_search(pshm_info_t * look)217 pshm_cache_search(pshm_info_t *look)
218 {
219 	PSHM_SUBSYS_ASSERT_HELD();
220 	return RB_FIND(pshmhead, &pshm_head, look);
221 }
222 
223 /*
224  * Add a new entry to the cache.
225  */
226 static void
pshm_cache_add(pshm_info_t * entry)227 pshm_cache_add(pshm_info_t *entry)
228 {
229 	pshm_info_t *conflict;
230 
231 	PSHM_SUBSYS_ASSERT_HELD();
232 	conflict = RB_INSERT(pshmhead, &pshm_head, entry);
233 	if (conflict != NULL) {
234 		panic("pshm_cache_add() found %p", conflict);
235 	}
236 	pshmnument++;
237 }
238 
239 /*
240  * Remove the given entry from the red black tree.
241  */
242 static void
pshm_cache_delete(pshm_info_t * entry)243 pshm_cache_delete(pshm_info_t *entry)
244 {
245 	PSHM_SUBSYS_ASSERT_HELD();
246 	assert(!(entry->pshm_flags & PSHM_REMOVED));
247 	RB_REMOVE(pshmhead, &pshm_head, entry);
248 	pshmnument--;
249 }
250 
251 /*
252  * Initialize the red black tree.
253  */
254 void
pshm_cache_init(void)255 pshm_cache_init(void)
256 {
257 	RB_INIT(&pshm_head);
258 }
259 
260 /*
261  * Invalidate all entries and delete all objects associated with them
262  * XXX - due to the reference counting, this only works if all userland
263  * references to it via file descriptors are also closed already. Is this
264  * known to be called after all user processes are killed?
265  */
266 int
pshm_cache_purge_all(void)267 pshm_cache_purge_all(void)
268 {
269 	pshm_info_t *p;
270 	pshm_info_t *tmp;
271 
272 	if (kauth_cred_issuser(kauth_cred_get()) == 0) {
273 		return EPERM;
274 	}
275 
276 	PSHM_SUBSYS_LOCK();
277 	RB_FOREACH_SAFE(p, pshmhead, &pshm_head, tmp) {
278 		pshm_unlink_internal(p);
279 	}
280 	assert(pshmnument == 0);
281 	PSHM_SUBSYS_UNLOCK();
282 
283 	return 0;
284 }
285 
286 int
pshm_cache_purge_uid(uid_t uid)287 pshm_cache_purge_uid(uid_t uid)
288 {
289 	pshm_info_t *p;
290 	pshm_info_t *tmp;
291 
292 	if (kauth_cred_issuser(kauth_cred_get()) == 0) {
293 		return EPERM;
294 	}
295 
296 	PSHM_SUBSYS_LOCK();
297 	RB_FOREACH_SAFE(p, pshmhead, &pshm_head, tmp) {
298 		if (p->pshm_uid == uid) {
299 			pshm_unlink_internal(p);
300 		}
301 	}
302 	PSHM_SUBSYS_UNLOCK();
303 
304 	return 0;
305 }
306 
307 /*
308  * Utility to get the shared memory name from userspace and
309  * populate a pshm_info_t with it. If there's a problem
310  * reading the name or it's malformed, will return an error code.
311  */
312 static int
pshm_get_name(pshm_info_t * pinfo,const user_addr_t user_addr)313 pshm_get_name(pshm_info_t *pinfo, const user_addr_t user_addr)
314 {
315 	size_t bytes_copied = 0;
316 	int error;
317 
318 
319 	error = copyinstr(user_addr, &pinfo->pshm_hdr.pshm_name[0], PSHMNAMLEN + 1, &bytes_copied);
320 	if (error != 0) {
321 		return error;
322 	}
323 	assert(bytes_copied <= PSHMNAMLEN + 1);
324 	assert(pinfo->pshm_hdr.pshm_name[bytes_copied - 1] == 0);
325 	if (bytes_copied < 2) { /* 2: expect at least one character and terminating zero */
326 		return EINVAL;
327 	}
328 	AUDIT_ARG(text, &pinfo->pshm_hdr.pshm_name[0]);
329 	return 0;
330 }
331 
332 /*
333  * Process a shm_open() system call.
334  */
335 int
shm_open(proc_t p,struct shm_open_args * uap,int32_t * retval)336 shm_open(proc_t p, struct shm_open_args *uap, int32_t *retval)
337 {
338 	int             indx;
339 	int             error = 0;
340 	pshm_info_t     *pinfo = NULL;
341 	pshm_info_t     *new_pinfo = NULL;
342 	pshmnode_t      *new_pnode = NULL;
343 	struct fileproc *fp = NULL;
344 	int             fmode;
345 	mode_t          cmode = (mode_t)uap->mode;
346 	bool            incache = false;
347 	bool            have_label = false;
348 
349 	AUDIT_ARG(fflags, uap->oflag);
350 	AUDIT_ARG(mode, cmode);
351 
352 	/*
353 	 * Allocate data structures we need. We parse the userspace name into
354 	 * a pshm_info_t, even when we don't need to O_CREAT.
355 	 */
356 	new_pinfo = kalloc_type(pshm_info_t, Z_WAITOK | Z_ZERO | Z_NOFAIL);
357 
358 	/*
359 	 * Get and check the name.
360 	 */
361 	error = pshm_get_name(new_pinfo, uap->name);
362 	if (error != 0) {
363 		goto bad;
364 	}
365 
366 	/*
367 	 * Attempt to allocate a new fp. If unsuccessful, the fp will be
368 	 * left unmodified (NULL).
369 	 */
370 	error = falloc(p, &fp, &indx, vfs_context_current());
371 	if (error) {
372 		goto bad;
373 	}
374 
375 	cmode &= ALLPERMS;
376 
377 	fmode = FFLAGS(uap->oflag);
378 	if ((fmode & (FREAD | FWRITE)) == 0) {
379 		error = EINVAL;
380 		goto bad;
381 	}
382 
383 	/*
384 	 * Will need a new pnode for the file pointer
385 	 */
386 	new_pnode = kalloc_type(pshmnode_t, Z_WAITOK | Z_ZERO);
387 	if (new_pnode == NULL) {
388 		error = ENOSPC;
389 		goto bad;
390 	}
391 
392 	/*
393 	 * If creating a new segment, fill in its information.
394 	 * If we find a pre-exisitng one in cache lookup we'll just toss this one later.
395 	 */
396 	if (fmode & O_CREAT) {
397 		new_pinfo->pshm_usecount = 2; /* one each for: file pointer, shm_unlink */
398 		new_pinfo->pshm_length = 0;
399 		new_pinfo->pshm_mode = cmode;
400 		new_pinfo->pshm_uid = kauth_getuid();
401 		new_pinfo->pshm_gid = kauth_getgid();
402 		SLIST_INIT(&new_pinfo->pshm_mobjs);
403 #if CONFIG_MACF
404 		mac_posixshm_label_init(&new_pinfo->pshm_hdr);
405 		have_label = true;
406 		error = mac_posixshm_check_create(kauth_cred_get(), new_pinfo->pshm_hdr.pshm_name);
407 		if (error) {
408 			goto bad;
409 		}
410 #endif
411 	}
412 
413 	/*
414 	 * Look up the named shared memory segment in the cache, possibly adding
415 	 * it for O_CREAT.
416 	 */
417 	PSHM_SUBSYS_LOCK();
418 
419 	pinfo = pshm_cache_search(new_pinfo);
420 	if (pinfo != NULL) {
421 		incache = true;
422 
423 		/* Get a new reference to go with the file pointer.*/
424 		error = pshm_ref(pinfo);
425 		if (error) {
426 			pinfo = NULL;      /* so cleanup code doesn't deref */
427 			goto bad_locked;
428 		}
429 
430 		/* can't have pre-existing if O_EXCL */
431 		if ((fmode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
432 			error = EEXIST;
433 			goto bad_locked;
434 		}
435 
436 		/* O_TRUNC is only valid while length is not yet set */
437 		if ((fmode & O_TRUNC) &&
438 		    (pinfo->pshm_flags & (PSHM_ALLOCATING | PSHM_ALLOCATED))) {
439 			error = EINVAL;
440 			goto bad_locked;
441 		}
442 	} else {
443 		incache = false;
444 
445 		/* if it wasn't found, must have O_CREAT */
446 		if (!(fmode & O_CREAT)) {
447 			error = ENOENT;
448 			goto bad_locked;
449 		}
450 
451 		/* Add the new region to the cache. */
452 		pinfo = new_pinfo;
453 		pshm_cache_add(pinfo);
454 		new_pinfo = NULL;       /* so that it doesn't get free'd */
455 	}
456 
457 	PSHM_SUBSYS_UNLOCK();
458 
459 	/*
460 	 * Check we have permission to access any pre-existing segment
461 	 */
462 	if (incache) {
463 		if (fmode & O_CREAT) {
464 			AUDIT_ARG(posix_ipc_perm, pinfo->pshm_uid,
465 			    pinfo->pshm_gid, pinfo->pshm_mode);
466 		}
467 #if CONFIG_MACF
468 		if ((error = mac_posixshm_check_open(kauth_cred_get(), &pinfo->pshm_hdr, fmode))) {
469 			goto bad;
470 		}
471 #endif
472 		if ((error = pshm_access(pinfo, fmode, kauth_cred_get(), p))) {
473 			goto bad;
474 		}
475 	} else {
476 #if CONFIG_MACF
477 		mac_posixshm_label_associate(kauth_cred_get(), &pinfo->pshm_hdr, pinfo->pshm_hdr.pshm_name);
478 #endif
479 	}
480 
481 	fp->fp_flags |= FP_CLOEXEC;
482 	fp->f_flag = fmode & FMASK;
483 	fp->f_ops = &pshmops;
484 	new_pnode->pinfo = pinfo;
485 	fp_set_data(fp, new_pnode);
486 
487 	proc_fdlock(p);
488 	procfdtbl_releasefd(p, indx, NULL);
489 	fp_drop(p, indx, fp, 1);
490 	proc_fdunlock(p);
491 
492 	*retval = indx;
493 	error = 0;
494 	goto done;
495 
496 bad_locked:
497 	PSHM_SUBSYS_UNLOCK();
498 bad:
499 	/*
500 	 * Drop any new reference to a pre-existing shared memory region.
501 	 */
502 	if (incache && pinfo != NULL) {
503 		PSHM_SUBSYS_LOCK();
504 		pshm_deref(pinfo);
505 		PSHM_SUBSYS_UNLOCK();
506 	}
507 
508 	/*
509 	 * Delete any allocated unused data structures.
510 	 */
511 	kfree_type(pshmnode_t, new_pnode);
512 
513 	if (fp != NULL) {
514 		fp_free(p, indx, fp);
515 	}
516 
517 done:
518 	if (new_pinfo != NULL) {
519 #if CONFIG_MACF
520 		if (have_label) {
521 			mac_posixshm_label_destroy(&new_pinfo->pshm_hdr);
522 		}
523 #endif
524 		kfree_type(pshm_info_t, new_pinfo);
525 	}
526 	return error;
527 }
528 
529 
530 /*
531  * The truncate call associates memory with shared memory region. It can
532  * only be succesfully done with a non-zero length once per shared memory region.
533  */
534 int
pshm_truncate(__unused proc_t p,struct fileproc * fp,__unused int fd,off_t length,__unused int32_t * retval)535 pshm_truncate(
536 	__unused proc_t       p,
537 	struct fileproc       *fp,
538 	__unused int          fd,
539 	off_t                 length,
540 	__unused int32_t      *retval)
541 {
542 	pshm_info_t           *pinfo;
543 	pshmnode_t            *pnode;
544 	kern_return_t         kret;
545 	mem_entry_name_port_t mem_object;
546 	mach_vm_size_t        total_size, alloc_size;
547 	memory_object_size_t  mosize;
548 	pshm_mobj_t           *pshmobj, *pshmobj_last;
549 	vm_map_t              user_map;
550 	int                   error;
551 
552 	user_map = current_map();
553 
554 	if (FILEGLOB_DTYPE(fp->fp_glob) != DTYPE_PSXSHM) {
555 		return EINVAL;
556 	}
557 
558 #if 0
559 	/*
560 	 * Can't enforce this yet, some third party tools don't
561 	 * specify O_RDWR like they ought to. See radar 48692182
562 	 */
563 	/* ftruncate() requires write permission */
564 	if (!(fp->f_flag & FWRITE)) {
565 		return EINVAL;
566 	}
567 #endif
568 
569 	PSHM_SUBSYS_LOCK();
570 	if (((pnode = (pshmnode_t *)fp_get_data(fp))) == NULL) {
571 		PSHM_SUBSYS_UNLOCK();
572 		return EINVAL;
573 	}
574 
575 	if ((pinfo = pnode->pinfo) == NULL) {
576 		PSHM_SUBSYS_UNLOCK();
577 		return EINVAL;
578 	}
579 
580 	/* We only allow one ftruncate() per lifetime of the shm object. */
581 	if (pinfo->pshm_flags & (PSHM_ALLOCATING | PSHM_ALLOCATED)) {
582 		PSHM_SUBSYS_UNLOCK();
583 		return EINVAL;
584 	}
585 
586 #if CONFIG_MACF
587 	error = mac_posixshm_check_truncate(kauth_cred_get(), &pinfo->pshm_hdr, length);
588 	if (error) {
589 		PSHM_SUBSYS_UNLOCK();
590 		return error;
591 	}
592 #endif
593 	/*
594 	 * Grab an extra reference, so we can drop the lock while allocating and
595 	 * ensure the objects don't disappear.
596 	 */
597 	error = pshm_ref(pinfo);
598 	if (error) {
599 		PSHM_SUBSYS_UNLOCK();
600 		return error;
601 	}
602 
603 	/* set ALLOCATING, so another truncate can't start */
604 	pinfo->pshm_flags |= PSHM_ALLOCATING;
605 	total_size = vm_map_round_page(length, vm_map_page_mask(user_map));
606 
607 	pshmobj_last = NULL;
608 	for (alloc_size = 0; alloc_size < total_size; alloc_size += mosize) {
609 		PSHM_SUBSYS_UNLOCK();
610 
611 		/* get a memory object back some of the shared memory */
612 		mosize = MIN(total_size - alloc_size, ANON_MAX_SIZE);
613 		kret = mach_make_memory_entry_64(VM_MAP_NULL, &mosize, 0,
614 		    MAP_MEM_NAMED_CREATE | VM_PROT_DEFAULT, &mem_object, 0);
615 
616 		if (kret != KERN_SUCCESS) {
617 			goto out;
618 		}
619 
620 		/* get a list entry to track the memory object */
621 		pshmobj = kalloc_type(pshm_mobj_t, Z_WAITOK | Z_NOFAIL);
622 
623 		PSHM_SUBSYS_LOCK();
624 
625 		/* link in the new entry */
626 		pshmobj->pshmo_memobject = (void *)mem_object;
627 		pshmobj->pshmo_size = mosize;
628 		SLIST_NEXT(pshmobj, pshmo_next) = NULL;
629 
630 		if (pshmobj_last == NULL) {
631 			SLIST_FIRST(&pinfo->pshm_mobjs) = pshmobj;
632 		} else {
633 			SLIST_INSERT_AFTER(pshmobj_last, pshmobj, pshmo_next);
634 		}
635 		pshmobj_last = pshmobj;
636 	}
637 
638 	/* all done, change flags to ALLOCATED and return success */
639 	pinfo->pshm_flags |= PSHM_ALLOCATED;
640 	pinfo->pshm_flags &= ~(PSHM_ALLOCATING);
641 	pinfo->pshm_length = total_size;
642 	pshm_deref(pinfo);              /* drop the "allocating" reference */
643 	PSHM_SUBSYS_UNLOCK();
644 	return 0;
645 
646 out:
647 	/* clean up any partially allocated objects */
648 	PSHM_SUBSYS_LOCK();
649 	while ((pshmobj = SLIST_FIRST(&pinfo->pshm_mobjs)) != NULL) {
650 		SLIST_REMOVE_HEAD(&pinfo->pshm_mobjs, pshmo_next);
651 		PSHM_SUBSYS_UNLOCK();
652 		mach_memory_entry_port_release(pshmobj->pshmo_memobject);
653 		kfree_type(pshm_mobj_t, pshmobj);
654 		PSHM_SUBSYS_LOCK();
655 	}
656 	pinfo->pshm_flags &= ~PSHM_ALLOCATING;
657 	pshm_deref(pinfo);              /* drop the "allocating" reference */
658 	PSHM_SUBSYS_UNLOCK();
659 
660 	switch (kret) {
661 	case KERN_INVALID_ADDRESS:
662 	case KERN_NO_SPACE:
663 		return ENOMEM;
664 	case KERN_PROTECTION_FAILURE:
665 		return EACCES;
666 	default:
667 		return EINVAL;
668 	}
669 }
670 
671 int
pshm_stat(pshmnode_t * pnode,void * ub,int isstat64)672 pshm_stat(pshmnode_t *pnode, void *ub, int isstat64)
673 {
674 	struct stat *sb = (struct stat *)0;     /* warning avoidance ; protected by isstat64 */
675 	struct stat64 * sb64 = (struct stat64 *)0;  /* warning avoidance ; protected by isstat64 */
676 	pshm_info_t *pinfo;
677 #if CONFIG_MACF
678 	int error;
679 #endif
680 
681 	PSHM_SUBSYS_LOCK();
682 	if ((pinfo = pnode->pinfo) == NULL) {
683 		PSHM_SUBSYS_UNLOCK();
684 		return EINVAL;
685 	}
686 
687 #if CONFIG_MACF
688 	error = mac_posixshm_check_stat(kauth_cred_get(), &pinfo->pshm_hdr);
689 	if (error) {
690 		PSHM_SUBSYS_UNLOCK();
691 		return error;
692 	}
693 #endif
694 
695 	if (isstat64 != 0) {
696 		sb64 = (struct stat64 *)ub;
697 		bzero(sb64, sizeof(struct stat64));
698 		sb64->st_mode = pinfo->pshm_mode;
699 		sb64->st_uid = pinfo->pshm_uid;
700 		sb64->st_gid = pinfo->pshm_gid;
701 		sb64->st_size = pinfo->pshm_length;
702 	} else {
703 		sb = (struct stat *)ub;
704 		bzero(sb, sizeof(struct stat));
705 		sb->st_mode = pinfo->pshm_mode;
706 		sb->st_uid = pinfo->pshm_uid;
707 		sb->st_gid = pinfo->pshm_gid;
708 		sb->st_size = pinfo->pshm_length;
709 	}
710 	PSHM_SUBSYS_UNLOCK();
711 
712 	return 0;
713 }
714 
715 /*
716  * Verify access to a shared memory region.
717  */
718 static int
pshm_access(pshm_info_t * pinfo,int mode,kauth_cred_t cred,__unused proc_t p)719 pshm_access(pshm_info_t *pinfo, int mode, kauth_cred_t cred, __unused proc_t p)
720 {
721 	mode_t mode_req = ((mode & FREAD) ? S_IRUSR : 0) |
722 	    ((mode & FWRITE) ? S_IWUSR : 0);
723 
724 	/* Otherwise, user id 0 always gets access. */
725 	if (!suser(cred, NULL)) {
726 		return 0;
727 	}
728 
729 	return posix_cred_access(cred, pinfo->pshm_uid, pinfo->pshm_gid, pinfo->pshm_mode, mode_req);
730 }
731 
732 int
pshm_mmap(__unused proc_t p,struct mmap_args * uap,user_addr_t * retval,struct fileproc * fp,off_t pageoff)733 pshm_mmap(
734 	__unused proc_t    p,
735 	struct mmap_args   *uap,
736 	user_addr_t        *retval,
737 	struct fileproc    *fp,
738 	off_t              pageoff)
739 {
740 	vm_map_offset_t    user_addr = (vm_map_offset_t)uap->addr;
741 	vm_map_size_t      user_size = (vm_map_size_t)uap->len;
742 	vm_map_offset_t    user_start_addr = 0;
743 	vm_map_size_t      map_size, mapped_size, pshm_size;
744 	int                prot = uap->prot;
745 	int                max_prot = VM_PROT_DEFAULT;
746 	int                flags = uap->flags;
747 	vm_object_offset_t file_pos = (vm_object_offset_t)uap->pos;
748 	vm_object_offset_t map_pos;
749 	vm_map_t           user_map;
750 	vm_map_kernel_flags_t vmk_flags;
751 	bool               docow;
752 	kern_return_t      kret = KERN_SUCCESS;
753 	pshm_info_t        *pinfo;
754 	pshmnode_t         *pnode;
755 	pshm_mobj_t        *pshmobj;
756 	int                error;
757 
758 	if (user_size == 0) {
759 		return 0;
760 	}
761 
762 	if (!(flags & MAP_SHARED)) {
763 		return EINVAL;
764 	}
765 
766 	/* Can't allow write permission if the shm_open() didn't allow them. */
767 	if (!(fp->f_flag & FWRITE)) {
768 		if (prot & VM_PROT_WRITE) {
769 			return EPERM;
770 		}
771 		max_prot &= ~VM_PROT_WRITE;
772 	}
773 
774 	user_map = current_map();
775 
776 	PSHM_SUBSYS_LOCK();
777 	pnode = (pshmnode_t *)fp_get_data(fp);
778 	if (pnode == NULL) {
779 		PSHM_SUBSYS_UNLOCK();
780 		return EINVAL;
781 	}
782 
783 	pinfo = pnode->pinfo;
784 	if (pinfo == NULL) {
785 		PSHM_SUBSYS_UNLOCK();
786 		return EINVAL;
787 	}
788 
789 	if (!(pinfo->pshm_flags & PSHM_ALLOCATED)) {
790 		PSHM_SUBSYS_UNLOCK();
791 		return EINVAL;
792 	}
793 
794 	pshm_size = vm_map_round_page((vm_map_size_t)pinfo->pshm_length, vm_map_page_mask(user_map));
795 
796 	if (user_size > pshm_size) {
797 		PSHM_SUBSYS_UNLOCK();
798 		return EINVAL;
799 	}
800 
801 	vm_map_size_t end_pos = 0;
802 	if (os_add_overflow(user_size, file_pos, &end_pos)) {
803 		PSHM_SUBSYS_UNLOCK();
804 		return EINVAL;
805 	}
806 	if (end_pos > pshm_size) {
807 		PSHM_SUBSYS_UNLOCK();
808 		return EINVAL;
809 	}
810 
811 	pshmobj = SLIST_FIRST(&pinfo->pshm_mobjs);
812 	if (pshmobj == NULL) {
813 		PSHM_SUBSYS_UNLOCK();
814 		return EINVAL;
815 	}
816 
817 #if CONFIG_MACF
818 	error = mac_posixshm_check_mmap(kauth_cred_get(), &pinfo->pshm_hdr, prot, flags);
819 	if (error) {
820 		PSHM_SUBSYS_UNLOCK();
821 		return error;
822 	}
823 #endif
824 	/* Grab an extra reference, so we can drop the lock while mapping. */
825 	error = pshm_ref(pinfo);
826 	if (error) {
827 		PSHM_SUBSYS_UNLOCK();
828 		return error;
829 	}
830 
831 	PSHM_SUBSYS_UNLOCK();
832 
833 	if (!(flags & MAP_FIXED)) {
834 		user_addr = vm_map_round_page(user_addr,
835 		    vm_map_page_mask(user_map));
836 		vmk_flags = VM_MAP_KERNEL_FLAGS_ANYWHERE();
837 	} else {
838 		if (user_addr != vm_map_round_page(user_addr,
839 		    vm_map_page_mask(user_map))) {
840 			error = EINVAL;
841 			goto out_deref;
842 		}
843 
844 		/*
845 		 * We do not get rid of the existing mappings here because
846 		 * it wouldn't be atomic (see comment in mmap()).  We let
847 		 * Mach VM know that we want it to replace any existing
848 		 * mapping with the new one.
849 		 */
850 		vmk_flags = VM_MAP_KERNEL_FLAGS_FIXED(.vmf_overwrite = true);
851 	}
852 	docow = false;
853 
854 	mapped_size = 0;
855 	/* reserve the entire space first... */
856 	kret = vm_map_enter_mem_object(user_map,
857 	    &user_addr,
858 	    user_size,
859 	    0,
860 	    vmk_flags,
861 	    IPC_PORT_NULL,
862 	    0,
863 	    false,
864 	    VM_PROT_NONE,
865 	    VM_PROT_NONE,
866 	    VM_INHERIT_NONE);
867 	user_start_addr = user_addr;
868 	if (kret != KERN_SUCCESS) {
869 		goto out_deref;
870 	}
871 
872 	/* Now overwrite with the real mappings. */
873 	for (map_pos = 0, pshmobj = SLIST_FIRST(&pinfo->pshm_mobjs);
874 	    user_size != 0;
875 	    map_pos += pshmobj->pshmo_size, pshmobj = SLIST_NEXT(pshmobj, pshmo_next)) {
876 		if (pshmobj == NULL) {
877 			/* nothing there to map !? */
878 			goto out_deref;
879 		}
880 		if (file_pos >= map_pos + pshmobj->pshmo_size) {
881 			continue;
882 		}
883 		map_size = (vm_map_size_t)(pshmobj->pshmo_size - (file_pos - map_pos));
884 		if (map_size > user_size) {
885 			map_size = user_size;
886 		}
887 
888 		kret = vm_map_enter_mem_object(
889 			user_map,
890 			&user_addr,
891 			map_size,
892 			0,
893 			VM_MAP_KERNEL_FLAGS_FIXED(.vmf_overwrite = true),
894 			pshmobj->pshmo_memobject,
895 			file_pos - map_pos,
896 			docow,
897 			prot,
898 			max_prot,
899 			VM_INHERIT_SHARE);
900 		if (kret != KERN_SUCCESS) {
901 			goto out_deref;
902 		}
903 
904 		user_addr += map_size;
905 		user_size -= map_size;
906 		mapped_size += map_size;
907 		file_pos += map_size;
908 	}
909 
910 	PSHM_SUBSYS_LOCK();
911 	pnode->mapp_addr = user_start_addr;
912 	pinfo->pshm_flags |= (PSHM_MAPPED | PSHM_INUSE);
913 	PSHM_SUBSYS_UNLOCK();
914 out_deref:
915 	PSHM_SUBSYS_LOCK();
916 	pshm_deref(pinfo);      /* drop the extra reference we had while mapping. */
917 	PSHM_SUBSYS_UNLOCK();
918 	if (kret != KERN_SUCCESS) {
919 		if (mapped_size != 0) {
920 			(void) mach_vm_deallocate(current_map(),
921 			    user_start_addr,
922 			    mapped_size);
923 		}
924 	}
925 
926 	switch (kret) {
927 	case KERN_SUCCESS:
928 		*retval = (user_addr_t)(user_start_addr + pageoff);
929 		return 0;
930 	case KERN_INVALID_ADDRESS:
931 	case KERN_NO_SPACE:
932 		return ENOMEM;
933 	case KERN_PROTECTION_FAILURE:
934 		return EACCES;
935 	default:
936 		return EINVAL;
937 	}
938 }
939 
940 /*
941  * Remove a shared memory region name from the name lookup cache.
942  */
943 static void
pshm_unlink_internal(pshm_info_t * pinfo)944 pshm_unlink_internal(pshm_info_t *pinfo)
945 {
946 	PSHM_SUBSYS_ASSERT_HELD();
947 
948 	pshm_cache_delete(pinfo);
949 	pinfo->pshm_flags |= PSHM_REMOVED;
950 
951 	/* release the "unlink" reference */
952 	pshm_deref(pinfo);
953 }
954 
955 int
shm_unlink(proc_t p,struct shm_unlink_args * uap,__unused int32_t * retval)956 shm_unlink(proc_t p, struct shm_unlink_args *uap, __unused int32_t *retval)
957 {
958 	int         error = 0;
959 	pshm_info_t *pinfo = NULL;
960 	pshm_info_t *name_pinfo = NULL;
961 
962 	/*
963 	 * Get the name from user args.
964 	 */
965 	name_pinfo = kalloc_type(pshm_info_t,
966 	    Z_WAITOK | Z_ZERO | Z_NOFAIL);
967 	error = pshm_get_name(name_pinfo, uap->name);
968 	if (error != 0) {
969 		error = EINVAL;
970 		goto bad;
971 	}
972 
973 	PSHM_SUBSYS_LOCK();
974 	pinfo = pshm_cache_search(name_pinfo);
975 
976 	if (pinfo == NULL) {
977 		error = ENOENT;
978 		goto bad_unlock;
979 	}
980 
981 #if CONFIG_MACF
982 	error = mac_posixshm_check_unlink(kauth_cred_get(), &pinfo->pshm_hdr, name_pinfo->pshm_hdr.pshm_name);
983 	if (error) {
984 		goto bad_unlock;
985 	}
986 #endif
987 
988 	AUDIT_ARG(posix_ipc_perm, pinfo->pshm_uid, pinfo->pshm_gid, pinfo->pshm_mode);
989 
990 	/*
991 	 * Following file semantics, unlink should normally be allowed
992 	 * for users with write permission only. We also allow the creator
993 	 * of a segment to be able to delete, even w/o write permission.
994 	 * That's because there's no equivalent of write permission for the
995 	 * directory containing a file.
996 	 */
997 	error = pshm_access(pinfo, FWRITE, kauth_cred_get(), p);
998 	if (error != 0 && pinfo->pshm_uid != kauth_getuid()) {
999 		goto bad_unlock;
1000 	}
1001 
1002 	pshm_unlink_internal(pinfo);
1003 	error = 0;
1004 bad_unlock:
1005 	PSHM_SUBSYS_UNLOCK();
1006 bad:
1007 	kfree_type(pshm_info_t, name_pinfo);
1008 	return error;
1009 }
1010 
1011 /*
1012  * Add a new reference to a shared memory region.
1013  * Fails if we will overflow the reference counter.
1014  */
1015 static int
pshm_ref(pshm_info_t * pinfo)1016 pshm_ref(pshm_info_t *pinfo)
1017 {
1018 	PSHM_SUBSYS_ASSERT_HELD();
1019 
1020 	if (pinfo->pshm_usecount == PSHM_MAXCOUNT) {
1021 		return EMFILE;
1022 	}
1023 	pinfo->pshm_usecount++;
1024 	return 0;
1025 }
1026 
1027 /*
1028  * Dereference a pshm_info_t. Delete the region if
1029  * this was the final reference count.
1030  */
1031 static void
pshm_deref(pshm_info_t * pinfo)1032 pshm_deref(pshm_info_t *pinfo)
1033 {
1034 	pshm_mobj_t *pshmobj;
1035 
1036 	PSHM_SUBSYS_ASSERT_HELD();
1037 	if (pinfo->pshm_usecount == 0) {
1038 		panic("negative usecount in pshm_close");
1039 	}
1040 	pinfo->pshm_usecount--; /* release this fd's reference */
1041 
1042 	if (pinfo->pshm_usecount == 0) {
1043 #if CONFIG_MACF
1044 		mac_posixshm_label_destroy(&pinfo->pshm_hdr);
1045 #endif
1046 		PSHM_SUBSYS_UNLOCK();
1047 
1048 		/*
1049 		 * Release references to any backing objects.
1050 		 */
1051 		while ((pshmobj = SLIST_FIRST(&pinfo->pshm_mobjs)) != NULL) {
1052 			SLIST_REMOVE_HEAD(&pinfo->pshm_mobjs, pshmo_next);
1053 			mach_memory_entry_port_release(pshmobj->pshmo_memobject);
1054 			kfree_type(pshm_mobj_t, pshmobj);
1055 		}
1056 
1057 		/* free the pinfo itself */
1058 		kfree_type(pshm_info_t, pinfo);
1059 
1060 		PSHM_SUBSYS_LOCK();
1061 	}
1062 }
1063 
1064 /* vfs_context_t passed to match prototype for struct fileops */
1065 static int
pshm_closefile(struct fileglob * fg,__unused vfs_context_t ctx)1066 pshm_closefile(struct fileglob *fg, __unused vfs_context_t ctx)
1067 {
1068 	int        error = EINVAL;
1069 	pshmnode_t *pnode;
1070 
1071 	PSHM_SUBSYS_LOCK();
1072 
1073 	pnode = (pshmnode_t *)fg_get_data(fg);
1074 	if (pnode != NULL) {
1075 		error = 0;
1076 		fg_set_data(fg, NULL); /* set fg_data to NULL to avoid racing close()es */
1077 		if (pnode->pinfo != NULL) {
1078 			pshm_deref(pnode->pinfo);
1079 			pnode->pinfo = NULL;
1080 		}
1081 	}
1082 
1083 	PSHM_SUBSYS_UNLOCK();
1084 	kfree_type(pshmnode_t, pnode);
1085 
1086 	return error;
1087 }
1088 
1089 int
fill_pshminfo(pshmnode_t * pshm,struct pshm_info * info)1090 fill_pshminfo(pshmnode_t * pshm, struct pshm_info * info)
1091 {
1092 	pshm_info_t *pinfo;
1093 	struct vinfo_stat *sb;
1094 
1095 	PSHM_SUBSYS_LOCK();
1096 	if ((pinfo = pshm->pinfo) == NULL) {
1097 		PSHM_SUBSYS_UNLOCK();
1098 		return EINVAL;
1099 	}
1100 
1101 	sb = &info->pshm_stat;
1102 
1103 	bzero(sb, sizeof(struct vinfo_stat));
1104 	sb->vst_mode = pinfo->pshm_mode;
1105 	sb->vst_uid = pinfo->pshm_uid;
1106 	sb->vst_gid = pinfo->pshm_gid;
1107 	sb->vst_size = pinfo->pshm_length;
1108 
1109 	info->pshm_mappaddr = pshm->mapp_addr;
1110 	bcopy(&pinfo->pshm_hdr.pshm_name[0], &info->pshm_name[0], PSHMNAMLEN + 1);
1111 
1112 	PSHM_SUBSYS_UNLOCK();
1113 	return 0;
1114 }
1115 
1116 #if CONFIG_MACF
1117 void
pshm_label_associate(struct fileproc * fp,struct vnode * vp,vfs_context_t ctx)1118 pshm_label_associate(struct fileproc *fp, struct vnode *vp, vfs_context_t ctx)
1119 {
1120 	pshmnode_t *pnode;
1121 	pshm_info_t *pshm;
1122 
1123 	PSHM_SUBSYS_LOCK();
1124 	pnode = (pshmnode_t *)fp_get_data(fp);
1125 	if (pnode != NULL) {
1126 		pshm = pnode->pinfo;
1127 		if (pshm != NULL) {
1128 			mac_posixshm_vnode_label_associate(
1129 				vfs_context_ucred(ctx), &pshm->pshm_hdr,
1130 				mac_posixshm_label(&pshm->pshm_hdr), vp, mac_vnode_label(vp));
1131 		}
1132 	}
1133 	PSHM_SUBSYS_UNLOCK();
1134 }
1135 #endif
1136