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