2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Copyright (c) 1990, 1996-1998 Apple Computer, Inc.
30 * All Rights Reserved.
33 * posix_shm.c : Support for POSIX shared memory APIs
36 * Author: Ananthakrishna Ramesh
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,
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>
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>
65 #include <sys/malloc.h>
68 #include <sys/sysproto.h>
69 #include <sys/proc_info.h>
70 #include <sys/posix_shm.h>
71 #include <security/audit/audit.h>
75 #include <security/mac_framework.h>
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 #include <mach/memory_object_control.h>
86 #include <vm/vm_map.h>
87 #include <vm/vm_protos.h>
89 #define f_flag f_fglob->fg_flag
90 #define f_type f_fglob->fg_ops->fo_type
91 #define f_msgcount f_fglob->fg_msgcount
92 #define f_cred f_fglob->fg_cred
93 #define f_ops f_fglob->fg_ops
94 #define f_offset f_fglob->fg_offset
95 #define f_data f_fglob->fg_data
98 * Used to construct the list of memory objects
99 * assigned to a populated shared memory segment.
101 typedef struct pshm_mobj
{
102 void *pshmo_memobject
;
103 memory_object_size_t pshmo_size
;
104 SLIST_ENTRY(pshm_mobj
) pshmo_next
;
108 * This represents an existing Posix shared memory object.
110 * It comes into existence with a shm_open(...O_CREAT...)
111 * call and goes away only after it has been shm_unlink()ed
112 * and the last remaining shm_open() file reference is closed.
114 * To keep track of that lifetime, pshm_usecount is used as a reference
115 * counter. It's incremented for every successful shm_open() and
116 * one extra time for the shm_unlink() to release. Internally
117 * you can temporarily use an additional reference whenever the
118 * subsystem lock has to be dropped for other reasons.
120 typedef struct internal_pshminfo
{
121 struct pshminfo pshm_hdr
;
122 SLIST_HEAD(pshm_mobjhead
, pshm_mobj
) pshm_mobjs
;
123 RB_ENTRY(internal_pshminfo
) pshm_links
; /* links for red/black tree */
125 #define pshm_flags pshm_hdr.pshm_flags
126 #define pshm_usecount pshm_hdr.pshm_usecount
127 #define pshm_length pshm_hdr.pshm_length
128 #define pshm_mode pshm_hdr.pshm_mode
129 #define pshm_uid pshm_hdr.pshm_uid
130 #define pshm_gid pshm_hdr.pshm_gid
131 #define pshm_label pshm_hdr.pshm_label
133 /* Values for pshm_flags that are still used */
134 #define PSHM_ALLOCATED 0x004 /* backing storage is allocated */
135 #define PSHM_MAPPED 0x008 /* mapped at least once */
136 #define PSHM_INUSE 0x010 /* mapped at least once */
137 #define PSHM_REMOVED 0x020 /* no longer in the name cache due to shm_unlink() */
138 #define PSHM_ALLOCATING 0x100 /* storage is being allocated */
141 * These handle reference counting pshm_info_t structs using pshm_usecount.
143 static int pshm_ref(pshm_info_t
*pinfo
);
144 static void pshm_deref(pshm_info_t
*pinfo
);
145 #define PSHM_MAXCOUNT UINT_MAX
148 * For every shm_open, we get a new one of these.
149 * The only reason we don't just use pshm_info directly is that
150 * you can query the mapped memory objects via proc_pidinfo to
151 * query the mapped address. Note that even this is a hack. If
152 * you mmap() the same fd multiple times, we only save/report
155 typedef struct pshmnode
{
161 /* compare function for the red black tree */
163 pshm_compare(pshm_info_t
*a
, pshm_info_t
*b
)
165 int cmp
= strncmp(a
->pshm_hdr
.pshm_name
, b
->pshm_hdr
.pshm_name
, PSHMNAMLEN
+ 1);
178 * shared memory "paths" are stored in a red black tree for lookup
180 u_long pshmnument
; /* count of entries allocated in the red black tree */
181 RB_HEAD(pshmhead
, internal_pshminfo
) pshm_head
;
182 RB_PROTOTYPE(pshmhead
, internal_pshminfo
, pshm_links
, pshm_compare
)
183 RB_GENERATE(pshmhead
, internal_pshminfo
, pshm_links
, pshm_compare
)
185 /* lookup, add, remove functions */
186 static pshm_info_t
*pshm_cache_search(pshm_info_t
* look
);
187 static void pshm_cache_add(pshm_info_t
*entry
);
188 static void pshm_cache_delete(pshm_info_t
*entry
);
190 static int pshm_closefile(struct fileglob
*fg
, vfs_context_t ctx
);
192 static int pshm_access(pshm_info_t
*pinfo
, int mode
, kauth_cred_t cred
, proc_t p
);
193 int pshm_cache_purge_all(proc_t p
);
195 static int pshm_unlink_internal(pshm_info_t
*pinfo
);
197 static const struct fileops pshmops
= {
198 .fo_type
= DTYPE_PSXSHM
,
199 .fo_read
= fo_no_read
,
200 .fo_write
= fo_no_write
,
201 .fo_ioctl
= fo_no_ioctl
,
202 .fo_select
= fo_no_select
,
203 .fo_close
= pshm_closefile
,
204 .fo_drain
= fo_no_drain
,
205 .fo_kqfilter
= fo_no_kqfilter
,
209 * Everything here is protected by a single mutex.
211 static lck_grp_t
*psx_shm_subsys_lck_grp
;
212 static lck_grp_attr_t
*psx_shm_subsys_lck_grp_attr
;
213 static lck_attr_t
*psx_shm_subsys_lck_attr
;
214 static lck_mtx_t psx_shm_subsys_mutex
;
216 #define PSHM_SUBSYS_LOCK() lck_mtx_lock(& psx_shm_subsys_mutex)
217 #define PSHM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_shm_subsys_mutex)
218 #define PSHM_SUBSYS_ASSERT_HELD() LCK_MTX_ASSERT(&psx_shm_subsys_mutex, LCK_MTX_ASSERT_OWNED)
221 __private_extern__
void
222 pshm_lock_init( void )
224 psx_shm_subsys_lck_grp_attr
= lck_grp_attr_alloc_init();
226 psx_shm_subsys_lck_grp
=
227 lck_grp_alloc_init("posix shared memory", psx_shm_subsys_lck_grp_attr
);
229 psx_shm_subsys_lck_attr
= lck_attr_alloc_init();
230 lck_mtx_init(&psx_shm_subsys_mutex
, psx_shm_subsys_lck_grp
, psx_shm_subsys_lck_attr
);
234 * Lookup an entry in the cache. Only the name is used from "look".
237 pshm_cache_search(pshm_info_t
*look
)
239 PSHM_SUBSYS_ASSERT_HELD();
240 return RB_FIND(pshmhead
, &pshm_head
, look
);
244 * Add a new entry to the cache.
247 pshm_cache_add(pshm_info_t
*entry
)
249 pshm_info_t
*conflict
;
251 PSHM_SUBSYS_ASSERT_HELD();
252 conflict
= RB_INSERT(pshmhead
, &pshm_head
, entry
);
253 if (conflict
!= NULL
) {
254 panic("pshm_cache_add() found %p", conflict
);
260 * Remove the given entry from the red black tree.
263 pshm_cache_delete(pshm_info_t
*entry
)
265 PSHM_SUBSYS_ASSERT_HELD();
266 assert(!(entry
->pshm_flags
& PSHM_REMOVED
));
267 RB_REMOVE(pshmhead
, &pshm_head
, entry
);
272 * Initialize the red black tree.
275 pshm_cache_init(void)
281 * Invalidate all entries and delete all objects associated with them
282 * XXX - due to the reference counting, this only works if all userland
283 * references to it via file descriptors are also closed already. Is this
284 * known to be called after all user processes are killed?
287 pshm_cache_purge_all(__unused proc_t proc
)
293 if (kauth_cred_issuser(kauth_cred_get()) == 0) {
298 RB_FOREACH_SAFE(p
, pshmhead
, &pshm_head
, tmp
) {
299 error
= pshm_unlink_internal(p
);
300 if (error
) { /* XXX: why give up on failure, should keep going */
304 assert(pshmnument
== 0);
307 PSHM_SUBSYS_UNLOCK();
310 printf("%s: Error %d removing posix shm cache: %ld remain!\n",
311 __func__
, error
, pshmnument
);
317 * Utility to get the shared memory name from userspace and
318 * populate a pshm_info_t with it. If there's a problem
319 * reading the name or it's malformed, will return an error code.
322 pshm_get_name(pshm_info_t
*pinfo
, const user_addr_t user_addr
)
324 size_t bytes_copied
= 0;
328 error
= copyinstr(user_addr
, &pinfo
->pshm_hdr
.pshm_name
[0], PSHMNAMLEN
+ 1, &bytes_copied
);
332 assert(bytes_copied
<= PSHMNAMLEN
+ 1);
333 assert(pinfo
->pshm_hdr
.pshm_name
[bytes_copied
- 1] == 0);
334 if (bytes_copied
< 2) { /* 2: expect at least one character and terminating zero */
337 AUDIT_ARG(text
, &pinfo
->pshm_hdr
.pshm_name
[0]);
342 * Process a shm_open() system call.
345 shm_open(proc_t p
, struct shm_open_args
*uap
, int32_t *retval
)
349 pshm_info_t
*pinfo
= NULL
;
350 pshm_info_t
*new_pinfo
= NULL
;
351 pshmnode_t
*new_pnode
= NULL
;
352 struct fileproc
*fp
= NULL
;
354 int cmode
= uap
->mode
;
355 bool incache
= false;
356 bool have_label
= false;
358 AUDIT_ARG(fflags
, uap
->oflag
);
359 AUDIT_ARG(mode
, uap
->mode
);
362 * Allocate data structures we need. We parse the userspace name into
363 * a pshm_info_t, even when we don't need to O_CREAT.
365 MALLOC(new_pinfo
, pshm_info_t
*, sizeof(pshm_info_t
), M_SHM
, M_WAITOK
| M_ZERO
);
366 if (new_pinfo
== NULL
) {
372 * Get and check the name.
374 error
= pshm_get_name(new_pinfo
, uap
->name
);
380 * Attempt to allocate a new fp. If unsuccessful, the fp will be
381 * left unmodified (NULL).
383 error
= falloc(p
, &fp
, &indx
, vfs_context_current());
390 fmode
= FFLAGS(uap
->oflag
);
391 if ((fmode
& (FREAD
| FWRITE
)) == 0) {
397 * Will need a new pnode for the file pointer
399 MALLOC(new_pnode
, pshmnode_t
*, sizeof(pshmnode_t
), M_SHM
, M_WAITOK
| M_ZERO
);
400 if (new_pnode
== NULL
) {
406 * If creating a new segment, fill in its information.
407 * If we find a pre-exisitng one in cache lookup we'll just toss this one later.
409 if (fmode
& O_CREAT
) {
410 new_pinfo
->pshm_usecount
= 2; /* one each for: file pointer, shm_unlink */
411 new_pinfo
->pshm_length
= 0;
412 new_pinfo
->pshm_mode
= cmode
;
413 new_pinfo
->pshm_uid
= kauth_getuid();
414 new_pinfo
->pshm_gid
= kauth_getgid();
415 SLIST_INIT(&new_pinfo
->pshm_mobjs
);
417 mac_posixshm_label_init(&new_pinfo
->pshm_hdr
);
419 error
= mac_posixshm_check_create(kauth_cred_get(), new_pinfo
->pshm_hdr
.pshm_name
);
427 * Look up the named shared memory segment in the cache, possibly adding
432 pinfo
= pshm_cache_search(new_pinfo
);
436 /* Get a new reference to go with the file pointer.*/
437 error
= pshm_ref(pinfo
);
439 pinfo
= NULL
; /* so cleanup code doesn't deref */
443 /* can't have pre-existing if O_EXCL */
444 if ((fmode
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
)) {
449 /* O_TRUNC is only valid while length is not yet set */
450 if ((fmode
& O_TRUNC
) &&
451 (pinfo
->pshm_flags
& (PSHM_ALLOCATING
| PSHM_ALLOCATED
))) {
458 /* if it wasn't found, must have O_CREAT */
459 if (!(fmode
& O_CREAT
)) {
464 /* Add the new region to the cache. */
466 pshm_cache_add(pinfo
);
467 new_pinfo
= NULL
; /* so that it doesn't get free'd */
470 PSHM_SUBSYS_UNLOCK();
473 * Check we have permission to access any pre-existing segment
476 if (fmode
& O_CREAT
) {
477 AUDIT_ARG(posix_ipc_perm
, pinfo
->pshm_uid
,
478 pinfo
->pshm_gid
, pinfo
->pshm_mode
);
481 if ((error
= mac_posixshm_check_open(kauth_cred_get(), &pinfo
->pshm_hdr
, fmode
))) {
485 if ((error
= pshm_access(pinfo
, fmode
, kauth_cred_get(), p
))) {
490 mac_posixshm_label_associate(kauth_cred_get(), &pinfo
->pshm_hdr
, pinfo
->pshm_hdr
.pshm_name
);
495 fp
->f_flag
= fmode
& FMASK
;
496 fp
->f_ops
= &pshmops
;
497 new_pnode
->pinfo
= pinfo
;
498 fp
->f_data
= (caddr_t
)new_pnode
;
499 *fdflags(p
, indx
) |= UF_EXCLOSE
;
500 procfdtbl_releasefd(p
, indx
, NULL
);
501 fp_drop(p
, indx
, fp
, 1);
509 PSHM_SUBSYS_UNLOCK();
512 * Drop any new reference to a pre-existing shared memory region.
514 if (incache
&& pinfo
!= NULL
) {
517 PSHM_SUBSYS_UNLOCK();
521 * Delete any allocated unused data structures.
523 if (new_pnode
!= NULL
) {
524 FREE(new_pnode
, M_SHM
);
528 fp_free(p
, indx
, fp
);
532 if (new_pinfo
!= NULL
) {
535 mac_posixshm_label_destroy(&new_pinfo
->pshm_hdr
);
538 FREE(new_pinfo
, M_SHM
);
545 * The truncate call associates memory with shared memory region. It can
546 * only be succesfully done with a non-zero length once per shared memory region.
554 __unused
int32_t *retval
)
559 mem_entry_name_port_t mem_object
;
560 mach_vm_size_t total_size
, alloc_size
;
561 memory_object_size_t mosize
;
562 pshm_mobj_t
*pshmobj
, *pshmobj_last
;
566 user_map
= current_map();
568 if (fp
->f_type
!= DTYPE_PSXSHM
) {
574 * Can't enforce this yet, some third party tools don't
575 * specify O_RDWR like they ought to. See radar 48692182
577 /* ftruncate() requires write permission */
578 if (!(fp
->f_flag
& FWRITE
)) {
584 if (((pnode
= (pshmnode_t
*)fp
->f_data
)) == NULL
) {
585 PSHM_SUBSYS_UNLOCK();
589 if ((pinfo
= pnode
->pinfo
) == NULL
) {
590 PSHM_SUBSYS_UNLOCK();
594 /* We only allow one ftruncate() per lifetime of the shm object. */
595 if (pinfo
->pshm_flags
& (PSHM_ALLOCATING
| PSHM_ALLOCATED
)) {
596 PSHM_SUBSYS_UNLOCK();
601 error
= mac_posixshm_check_truncate(kauth_cred_get(), &pinfo
->pshm_hdr
, length
);
603 PSHM_SUBSYS_UNLOCK();
608 * Grab an extra reference, so we can drop the lock while allocating and
609 * ensure the objects don't disappear.
611 error
= pshm_ref(pinfo
);
613 PSHM_SUBSYS_UNLOCK();
617 /* set ALLOCATING, so another truncate can't start */
618 pinfo
->pshm_flags
|= PSHM_ALLOCATING
;
619 total_size
= vm_map_round_page(length
, vm_map_page_mask(user_map
));
622 for (alloc_size
= 0; alloc_size
< total_size
; alloc_size
+= mosize
) {
623 PSHM_SUBSYS_UNLOCK();
625 /* get a memory object back some of the shared memory */
626 mosize
= MIN(total_size
- alloc_size
, ANON_MAX_SIZE
);
627 kret
= mach_make_memory_entry_64(VM_MAP_NULL
, &mosize
, 0,
628 MAP_MEM_NAMED_CREATE
| VM_PROT_DEFAULT
, &mem_object
, 0);
630 if (kret
!= KERN_SUCCESS
) {
634 /* get a list entry to track the memory object */
635 MALLOC(pshmobj
, pshm_mobj_t
*, sizeof(pshm_mobj_t
), M_SHM
, M_WAITOK
);
636 if (pshmobj
== NULL
) {
637 kret
= KERN_NO_SPACE
;
638 mach_memory_entry_port_release(mem_object
);
645 /* link in the new entry */
646 pshmobj
->pshmo_memobject
= (void *)mem_object
;
647 pshmobj
->pshmo_size
= mosize
;
648 SLIST_NEXT(pshmobj
, pshmo_next
) = NULL
;
650 if (pshmobj_last
== NULL
) {
651 SLIST_FIRST(&pinfo
->pshm_mobjs
) = pshmobj
;
653 SLIST_INSERT_AFTER(pshmobj_last
, pshmobj
, pshmo_next
);
655 pshmobj_last
= pshmobj
;
658 /* all done, change flags to ALLOCATED and return success */
659 pinfo
->pshm_flags
|= PSHM_ALLOCATED
;
660 pinfo
->pshm_flags
&= ~(PSHM_ALLOCATING
);
661 pinfo
->pshm_length
= total_size
;
662 pshm_deref(pinfo
); /* drop the "allocating" reference */
663 PSHM_SUBSYS_UNLOCK();
667 /* clean up any partially allocated objects */
669 while ((pshmobj
= SLIST_FIRST(&pinfo
->pshm_mobjs
)) != NULL
) {
670 SLIST_REMOVE_HEAD(&pinfo
->pshm_mobjs
, pshmo_next
);
671 PSHM_SUBSYS_UNLOCK();
672 mach_memory_entry_port_release(pshmobj
->pshmo_memobject
);
673 FREE(pshmobj
, M_SHM
);
676 pinfo
->pshm_flags
&= ~PSHM_ALLOCATING
;
677 pshm_deref(pinfo
); /* drop the "allocating" reference */
678 PSHM_SUBSYS_UNLOCK();
681 case KERN_INVALID_ADDRESS
:
684 case KERN_PROTECTION_FAILURE
:
692 pshm_stat(pshmnode_t
*pnode
, void *ub
, int isstat64
)
694 struct stat
*sb
= (struct stat
*)0; /* warning avoidance ; protected by isstat64 */
695 struct stat64
* sb64
= (struct stat64
*)0; /* warning avoidance ; protected by isstat64 */
702 if ((pinfo
= pnode
->pinfo
) == NULL
) {
703 PSHM_SUBSYS_UNLOCK();
708 error
= mac_posixshm_check_stat(kauth_cred_get(), &pinfo
->pshm_hdr
);
710 PSHM_SUBSYS_UNLOCK();
716 sb64
= (struct stat64
*)ub
;
717 bzero(sb64
, sizeof(struct stat64
));
718 sb64
->st_mode
= pinfo
->pshm_mode
;
719 sb64
->st_uid
= pinfo
->pshm_uid
;
720 sb64
->st_gid
= pinfo
->pshm_gid
;
721 sb64
->st_size
= pinfo
->pshm_length
;
723 sb
= (struct stat
*)ub
;
724 bzero(sb
, sizeof(struct stat
));
725 sb
->st_mode
= pinfo
->pshm_mode
;
726 sb
->st_uid
= pinfo
->pshm_uid
;
727 sb
->st_gid
= pinfo
->pshm_gid
;
728 sb
->st_size
= pinfo
->pshm_length
;
730 PSHM_SUBSYS_UNLOCK();
736 * Verify access to a shared memory region.
739 pshm_access(pshm_info_t
*pinfo
, int mode
, kauth_cred_t cred
, __unused proc_t p
)
741 int mode_req
= ((mode
& FREAD
) ? S_IRUSR
: 0) |
742 ((mode
& FWRITE
) ? S_IWUSR
: 0);
744 /* Otherwise, user id 0 always gets access. */
745 if (!suser(cred
, NULL
)) {
749 return posix_cred_access(cred
, pinfo
->pshm_uid
, pinfo
->pshm_gid
, pinfo
->pshm_mode
, mode_req
);
755 struct mmap_args
*uap
,
760 vm_map_offset_t user_addr
= (vm_map_offset_t
)uap
->addr
;
761 vm_map_size_t user_size
= (vm_map_size_t
)uap
->len
;
762 vm_map_offset_t user_start_addr
;
763 vm_map_size_t map_size
, mapped_size
;
764 int prot
= uap
->prot
;
765 int max_prot
= VM_PROT_DEFAULT
;
766 int flags
= uap
->flags
;
767 vm_object_offset_t file_pos
= (vm_object_offset_t
)uap
->pos
;
768 vm_object_offset_t map_pos
;
771 vm_map_kernel_flags_t vmk_flags
;
773 kern_return_t kret
= KERN_SUCCESS
;
776 pshm_mobj_t
*pshmobj
;
779 if (user_size
== 0) {
783 if (!(flags
& MAP_SHARED
)) {
787 /* Can't allow write permission if the shm_open() didn't allow them. */
788 if (!(fp
->f_flag
& FWRITE
)) {
789 if (prot
& VM_PROT_WRITE
) {
792 max_prot
&= ~VM_PROT_WRITE
;
796 pnode
= (pshmnode_t
*)fp
->f_data
;
798 PSHM_SUBSYS_UNLOCK();
802 pinfo
= pnode
->pinfo
;
804 PSHM_SUBSYS_UNLOCK();
808 if (!(pinfo
->pshm_flags
& PSHM_ALLOCATED
)) {
809 PSHM_SUBSYS_UNLOCK();
813 if (user_size
> (vm_map_size_t
)pinfo
->pshm_length
) {
814 PSHM_SUBSYS_UNLOCK();
818 vm_map_size_t end_pos
= 0;
819 if (os_add_overflow(user_size
, file_pos
, &end_pos
)) {
820 PSHM_SUBSYS_UNLOCK();
823 if (end_pos
> (vm_map_size_t
)pinfo
->pshm_length
) {
824 PSHM_SUBSYS_UNLOCK();
828 pshmobj
= SLIST_FIRST(&pinfo
->pshm_mobjs
);
829 if (pshmobj
== NULL
) {
830 PSHM_SUBSYS_UNLOCK();
835 error
= mac_posixshm_check_mmap(kauth_cred_get(), &pinfo
->pshm_hdr
, prot
, flags
);
837 PSHM_SUBSYS_UNLOCK();
841 /* Grab an extra reference, so we can drop the lock while mapping. */
842 error
= pshm_ref(pinfo
);
844 PSHM_SUBSYS_UNLOCK();
848 PSHM_SUBSYS_UNLOCK();
849 user_map
= current_map();
851 if (!(flags
& MAP_FIXED
)) {
852 alloc_flags
= VM_FLAGS_ANYWHERE
;
853 user_addr
= vm_map_round_page(user_addr
,
854 vm_map_page_mask(user_map
));
856 if (user_addr
!= vm_map_round_page(user_addr
,
857 vm_map_page_mask(user_map
))) {
863 * We do not get rid of the existing mappings here because
864 * it wouldn't be atomic (see comment in mmap()). We let
865 * Mach VM know that we want it to replace any existing
866 * mapping with the new one.
868 alloc_flags
= VM_FLAGS_FIXED
| VM_FLAGS_OVERWRITE
;
873 vmk_flags
= VM_MAP_KERNEL_FLAGS_NONE
;
874 /* reserve the entire space first... */
875 kret
= vm_map_enter_mem_object(user_map
,
888 user_start_addr
= user_addr
;
889 if (kret
!= KERN_SUCCESS
) {
893 /* Now overwrite with the real mappings. */
894 for (map_pos
= 0, pshmobj
= SLIST_FIRST(&pinfo
->pshm_mobjs
);
896 map_pos
+= pshmobj
->pshmo_size
, pshmobj
= SLIST_NEXT(pshmobj
, pshmo_next
)) {
897 if (pshmobj
== NULL
) {
898 /* nothing there to map !? */
901 if (file_pos
>= map_pos
+ pshmobj
->pshmo_size
) {
904 map_size
= pshmobj
->pshmo_size
- (file_pos
- map_pos
);
905 if (map_size
> user_size
) {
906 map_size
= user_size
;
908 vmk_flags
= VM_MAP_KERNEL_FLAGS_NONE
;
909 kret
= vm_map_enter_mem_object(
914 VM_FLAGS_FIXED
| VM_FLAGS_OVERWRITE
,
917 pshmobj
->pshmo_memobject
,
923 if (kret
!= KERN_SUCCESS
) {
927 user_addr
+= map_size
;
928 user_size
-= map_size
;
929 mapped_size
+= map_size
;
930 file_pos
+= map_size
;
934 pnode
->mapp_addr
= user_start_addr
;
935 pinfo
->pshm_flags
|= (PSHM_MAPPED
| PSHM_INUSE
);
936 PSHM_SUBSYS_UNLOCK();
939 pshm_deref(pinfo
); /* drop the extra reference we had while mapping. */
940 PSHM_SUBSYS_UNLOCK();
941 if (kret
!= KERN_SUCCESS
) {
942 if (mapped_size
!= 0) {
943 (void) mach_vm_deallocate(current_map(),
951 *retval
= (user_start_addr
+ pageoff
);
953 case KERN_INVALID_ADDRESS
:
956 case KERN_PROTECTION_FAILURE
:
964 * Remove a shared memory region name from the name lookup cache.
967 pshm_unlink_internal(pshm_info_t
*pinfo
)
969 PSHM_SUBSYS_ASSERT_HELD();
975 pshm_cache_delete(pinfo
);
976 pinfo
->pshm_flags
|= PSHM_REMOVED
;
978 /* release the "unlink" reference */
985 shm_unlink(proc_t p
, struct shm_unlink_args
*uap
, __unused
int32_t *retval
)
988 pshm_info_t
*pinfo
= NULL
;
989 pshm_info_t
*name_pinfo
= NULL
;
992 * Get the name from user args.
994 MALLOC(name_pinfo
, pshm_info_t
*, sizeof(pshm_info_t
), M_SHM
, M_WAITOK
| M_ZERO
);
995 if (name_pinfo
== NULL
) {
999 error
= pshm_get_name(name_pinfo
, uap
->name
);
1006 pinfo
= pshm_cache_search(name_pinfo
);
1008 if (pinfo
== NULL
) {
1014 error
= mac_posixshm_check_unlink(kauth_cred_get(), &pinfo
->pshm_hdr
, name_pinfo
->pshm_hdr
.pshm_name
);
1020 AUDIT_ARG(posix_ipc_perm
, pinfo
->pshm_uid
, pinfo
->pshm_gid
, pinfo
->pshm_mode
);
1023 * Following file semantics, unlink should normally be allowed
1024 * for users with write permission only. We also allow the creator
1025 * of a segment to be able to delete, even w/o write permission.
1026 * That's because there's no equivalent of write permission for the
1027 * directory containing a file.
1029 error
= pshm_access(pinfo
, FWRITE
, kauth_cred_get(), p
);
1030 if (error
!= 0 && pinfo
->pshm_uid
!= kauth_getuid()) {
1034 error
= pshm_unlink_internal(pinfo
);
1036 PSHM_SUBSYS_UNLOCK();
1038 if (name_pinfo
!= NULL
) {
1039 FREE(name_pinfo
, M_SHM
);
1045 * Add a new reference to a shared memory region.
1046 * Fails if we will overflow the reference counter.
1049 pshm_ref(pshm_info_t
*pinfo
)
1051 PSHM_SUBSYS_ASSERT_HELD();
1053 if (pinfo
->pshm_usecount
== PSHM_MAXCOUNT
) {
1056 pinfo
->pshm_usecount
++;
1061 * Dereference a pshm_info_t. Delete the region if
1062 * this was the final reference count.
1065 pshm_deref(pshm_info_t
*pinfo
)
1067 pshm_mobj_t
*pshmobj
;
1069 PSHM_SUBSYS_ASSERT_HELD();
1070 if (pinfo
->pshm_usecount
== 0) {
1071 panic("negative usecount in pshm_close\n");
1073 pinfo
->pshm_usecount
--; /* release this fd's reference */
1075 if (pinfo
->pshm_usecount
== 0) {
1077 mac_posixshm_label_destroy(&pinfo
->pshm_hdr
);
1079 PSHM_SUBSYS_UNLOCK();
1082 * Release references to any backing objects.
1084 while ((pshmobj
= SLIST_FIRST(&pinfo
->pshm_mobjs
)) != NULL
) {
1085 SLIST_REMOVE_HEAD(&pinfo
->pshm_mobjs
, pshmo_next
);
1086 mach_memory_entry_port_release(pshmobj
->pshmo_memobject
);
1087 FREE(pshmobj
, M_SHM
);
1090 /* free the pinfo itself */
1097 /* vfs_context_t passed to match prototype for struct fileops */
1099 pshm_closefile(struct fileglob
*fg
, __unused vfs_context_t ctx
)
1106 pnode
= (pshmnode_t
*)fg
->fg_data
;
1107 if (pnode
!= NULL
) {
1109 fg
->fg_data
= NULL
; /* set fg_data to NULL to avoid racing close()es */
1110 if (pnode
->pinfo
!= NULL
) {
1111 pshm_deref(pnode
->pinfo
);
1112 pnode
->pinfo
= NULL
;
1116 PSHM_SUBSYS_UNLOCK();
1117 if (pnode
!= NULL
) {
1125 fill_pshminfo(pshmnode_t
* pshm
, struct pshm_info
* info
)
1128 struct vinfo_stat
*sb
;
1131 if ((pinfo
= pshm
->pinfo
) == NULL
) {
1132 PSHM_SUBSYS_UNLOCK();
1136 sb
= &info
->pshm_stat
;
1138 bzero(sb
, sizeof(struct vinfo_stat
));
1139 sb
->vst_mode
= pinfo
->pshm_mode
;
1140 sb
->vst_uid
= pinfo
->pshm_uid
;
1141 sb
->vst_gid
= pinfo
->pshm_gid
;
1142 sb
->vst_size
= pinfo
->pshm_length
;
1144 info
->pshm_mappaddr
= pshm
->mapp_addr
;
1145 bcopy(&pinfo
->pshm_hdr
.pshm_name
[0], &info
->pshm_name
[0], PSHMNAMLEN
+ 1);
1147 PSHM_SUBSYS_UNLOCK();
1153 pshm_label_associate(struct fileproc
*fp
, struct vnode
*vp
, vfs_context_t ctx
)
1159 pnode
= (pshmnode_t
*)fp
->f_data
;
1160 if (pnode
!= NULL
) {
1161 pshm
= pnode
->pinfo
;
1163 mac_posixshm_vnode_label_associate(
1164 vfs_context_ucred(ctx
), &pshm
->pshm_hdr
, pshm
->pshm_label
,
1168 PSHM_SUBSYS_UNLOCK();