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