Print this page
OS-5223 removed shm segment is no longer available
Reviewed by: Bryan Cantrill <bryan@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
        
@@ -19,10 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
 /*        All Rights Reserved   */
 
@@ -317,10 +318,11 @@
                  */
                 uint_t  share_szc;
                 size_t  share_size;
                 struct  shm_data ssd;
                 uintptr_t align_hint;
+                long    curprot;
 
                 /*
                  * Pick a share pagesize to use, if (!isspt(sp)).
                  * Otherwise use the already chosen page size.
                  *
@@ -451,21 +453,22 @@
                                 as_rangeunlock(as);
                                 goto errret;
                         }
                 }
 
+                curprot = sp->shm_opts & SHM_PROT_MASK;
                 if (!isspt(sp)) {
                         error = sptcreate(size, &segspt, sp->shm_amp, prot,
                             flags, share_szc);
                         if (error) {
                                 as_rangeunlock(as);
                                 goto errret;
                         }
                         sp->shm_sptinfo->sptas = segspt->s_as;
                         sp->shm_sptseg = segspt;
-                        sp->shm_sptprot = prot;
-                } else if ((prot & sp->shm_sptprot) != sp->shm_sptprot) {
+                        sp->shm_opts = (sp->shm_opts & ~SHM_PROT_MASK) | prot;
+                } else if ((prot & curprot) != curprot) {
                         /*
                          * Ensure we're attaching to an ISM segment with
                          * fewer or equal permissions than what we're
                          * allowed.  Fail if the segment has more
                          * permissions than what we're allowed.
@@ -746,10 +749,27 @@
                 if (sp->shm_lkcnt && (--sp->shm_lkcnt == 0)) {
                         shmem_unlock(sp, sp->shm_amp);
                 }
                 break;
 
+        /* Stage segment for removal, but don't remove until last detach */
+        case SHM_RMID:
+                if ((error = secpolicy_ipc_owner(cr, (kipc_perm_t *)sp)) != 0)
+                        break;
+
+                /*
+                 * If attached, just mark it as a pending remove, otherwise
+                 * we must perform the normal ipc_rmid now.
+                 */
+                if ((sp->shm_perm.ipc_ref - 1) > 0) {
+                        sp->shm_opts |= SHM_RM_PENDING;
+                } else {
+                        mutex_exit(lock);
+                        return (ipc_rmid(shm_svc, shmid, cr));
+                }
+                break;
+
         default:
                 error = EINVAL;
                 break;
         }
         mutex_exit(lock);
@@ -776,10 +796,27 @@
         (void) ipc_lock(shm_svc, sp->shm_perm.ipc_id);
         if (sap->sa_flags & SHMSA_ISM)
                 sp->shm_ismattch--;
         sp->shm_dtime = gethrestime_sec();
         sp->shm_lpid = pp->p_pid;
+        if ((sp->shm_opts & SHM_RM_PENDING) != 0 &&
+            sp->shm_perm.ipc_ref == 2) {
+                /*
+                 * If this is the last detach of the segment across the whole
+                 * system then now we can perform the delayed IPC_RMID.
+                 * The ipc_ref count has 1 for the original 'get' and one for
+                 * each 'attach' (see 'stat' handling in shmctl).
+                 */
+                sp->shm_opts &= ~SHM_RM_PENDING;
+                mutex_enter(&shm_svc->ipcs_lock);
+                ipc_rmsvc(shm_svc, (kipc_perm_t *)sp);  /* Drops lock */
+                ASSERT(!MUTEX_HELD(&shm_svc->ipcs_lock));
+                ASSERT(((kipc_perm_t *)sp)->ipc_ref > 0);
+
+                /* Lock was dropped, need to retake it for following rele. */
+                (void) ipc_lock(shm_svc, sp->shm_perm.ipc_id);
+        }
         ipc_rele(shm_svc, (kipc_perm_t *)sp);   /* Drops lock */
 
         kmem_free(sap, sizeof (segacct_t));
 }