LCOV - code coverage report
Current view: top level - kern - sysv_sem.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 444 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 10 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: sysv_sem.c,v 1.53 2015/03/14 03:38:50 jsg Exp $       */
       2             : /*      $NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $   */
       3             : 
       4             : /*
       5             :  * Copyright (c) 2002,2003 Todd C. Miller <Todd.Miller@courtesan.com>
       6             :  *
       7             :  * Permission to use, copy, modify, and distribute this software for any
       8             :  * purpose with or without fee is hereby granted, provided that the above
       9             :  * copyright notice and this permission notice appear in all copies.
      10             :  *
      11             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      12             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      13             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      14             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      15             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      16             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      17             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      18             :  *
      19             :  * Sponsored in part by the Defense Advanced Research Projects
      20             :  * Agency (DARPA) and Air Force Research Laboratory, Air Force
      21             :  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
      22             :  */
      23             : /*
      24             :  * Implementation of SVID semaphores
      25             :  *
      26             :  * Author:  Daniel Boulet
      27             :  *
      28             :  * This software is provided ``AS IS'' without any warranties of any kind.
      29             :  */
      30             : 
      31             : #include <sys/param.h>
      32             : #include <sys/systm.h>
      33             : #include <sys/proc.h>
      34             : #include <sys/sem.h>
      35             : #include <sys/sysctl.h>
      36             : #include <sys/malloc.h>
      37             : #include <sys/pool.h>
      38             : 
      39             : #include <sys/mount.h>
      40             : #include <sys/syscallargs.h>
      41             : 
      42             : #ifdef SEM_DEBUG
      43             : #define DPRINTF(x)      printf x
      44             : #else
      45             : #define DPRINTF(x)
      46             : #endif
      47             : 
      48             : int     semtot = 0;
      49             : int     semutot = 0;
      50             : struct  semid_ds **sema;        /* semaphore id list */
      51             : SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
      52             : struct  pool sema_pool;         /* pool for struct semid_ds */
      53             : struct  pool semu_pool;         /* pool for struct sem_undo (SEMUSZ) */
      54             : unsigned short *semseqs;        /* array of sem sequence numbers */
      55             : 
      56             : struct sem_undo *semu_alloc(struct process *);
      57             : int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
      58             : void semundo_clear(int, int);
      59             : 
      60             : void
      61           0 : seminit(void)
      62             : {
      63             : 
      64           0 :         pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, PR_WAITOK,
      65             :             "semapl", NULL);
      66           0 :         pool_init(&semu_pool, SEMUSZ, 0, 0, PR_WAITOK, "semupl", NULL);
      67           0 :         sema = mallocarray(seminfo.semmni, sizeof(struct semid_ds *),
      68             :             M_SEM, M_WAITOK|M_ZERO);
      69           0 :         semseqs = mallocarray(seminfo.semmni, sizeof(unsigned short),
      70             :             M_SEM, M_WAITOK|M_ZERO);
      71           0 :         SLIST_INIT(&semu_list);
      72           0 : }
      73             : 
      74             : /*
      75             :  * Allocate a new sem_undo structure for a process
      76             :  * (returns ptr to structure or NULL if no more room)
      77             :  */
      78             : struct sem_undo *
      79           0 : semu_alloc(struct process *pr)
      80             : {
      81             :         struct sem_undo *suptr, *sutmp;
      82             : 
      83           0 :         if (semutot == seminfo.semmnu)
      84           0 :                 return (NULL);          /* no space */
      85             : 
      86             :         /*
      87             :          * Allocate a semu w/o waiting if possible.
      88             :          * If we do have to wait, we must check to verify that a semu
      89             :          * with un_proc == pr has not been allocated in the meantime.
      90             :          */
      91           0 :         semutot++;
      92           0 :         if ((suptr = pool_get(&semu_pool, PR_NOWAIT)) == NULL) {
      93           0 :                 sutmp = pool_get(&semu_pool, PR_WAITOK);
      94           0 :                 SLIST_FOREACH(suptr, &semu_list, un_next) {
      95           0 :                         if (suptr->un_proc == pr) {
      96           0 :                                 pool_put(&semu_pool, sutmp);
      97           0 :                                 semutot--;
      98           0 :                                 return (suptr);
      99             :                         }
     100             :                 }
     101             :                 suptr = sutmp;
     102           0 :         }
     103           0 :         suptr->un_cnt = 0;
     104           0 :         suptr->un_proc = pr;
     105           0 :         SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
     106           0 :         return (suptr);
     107           0 : }
     108             : 
     109             : /*
     110             :  * Adjust a particular entry for a particular proc
     111             :  */
     112             : int
     113           0 : semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
     114             :         int adjval)
     115             : {
     116           0 :         struct process *pr = p->p_p;
     117             :         struct sem_undo *suptr;
     118             :         struct undo *sunptr;
     119             :         int i;
     120             : 
     121             :         /*
     122             :          * Look for and remember the sem_undo if the caller doesn't provide it.
     123             :          */
     124           0 :         suptr = *supptr;
     125           0 :         if (suptr == NULL) {
     126           0 :                 SLIST_FOREACH(suptr, &semu_list, un_next) {
     127           0 :                         if (suptr->un_proc == pr) {
     128           0 :                                 *supptr = suptr;
     129           0 :                                 break;
     130             :                         }
     131             :                 }
     132           0 :                 if (suptr == NULL) {
     133           0 :                         if (adjval == 0)
     134           0 :                                 return (0);
     135           0 :                         suptr = semu_alloc(p->p_p);
     136           0 :                         if (suptr == NULL)
     137           0 :                                 return (ENOSPC);
     138           0 :                         *supptr = suptr;
     139           0 :                 }
     140             :         }
     141             : 
     142             :         /*
     143             :          * Look for the requested entry and adjust it
     144             :          * (delete if adjval becomes 0).
     145             :          */
     146           0 :         sunptr = &suptr->un_ent[0];
     147           0 :         for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
     148           0 :                 if (sunptr->un_id != semid || sunptr->un_num != semnum)
     149             :                         continue;
     150           0 :                 if (adjval == 0)
     151           0 :                         sunptr->un_adjval = 0;
     152             :                 else
     153           0 :                         sunptr->un_adjval += adjval;
     154           0 :                 if (sunptr->un_adjval != 0)
     155           0 :                         return (0);
     156             : 
     157           0 :                 if (--suptr->un_cnt == 0) {
     158           0 :                         SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next);
     159           0 :                         pool_put(&semu_pool, suptr);
     160           0 :                         semutot--;
     161           0 :                 } else if (i < suptr->un_cnt)
     162           0 :                         suptr->un_ent[i] =
     163           0 :                             suptr->un_ent[suptr->un_cnt];
     164           0 :                 return (0);
     165             :         }
     166             : 
     167             :         /* Didn't find the right entry - create it */
     168           0 :         if (adjval == 0)
     169           0 :                 return (0);
     170           0 :         if (suptr->un_cnt == SEMUME)
     171           0 :                 return (EINVAL);
     172             : 
     173           0 :         sunptr = &suptr->un_ent[suptr->un_cnt];
     174           0 :         suptr->un_cnt++;
     175           0 :         sunptr->un_adjval = adjval;
     176           0 :         sunptr->un_id = semid;
     177           0 :         sunptr->un_num = semnum;
     178           0 :         return (0);
     179           0 : }
     180             : 
     181             : void
     182           0 : semundo_clear(int semid, int semnum)
     183             : {
     184           0 :         struct sem_undo *suptr = SLIST_FIRST(&semu_list);
     185             :         struct sem_undo *suprev = NULL;
     186             :         struct undo *sunptr;
     187             :         int i;
     188             : 
     189           0 :         while (suptr != NULL) {
     190           0 :                 sunptr = &suptr->un_ent[0];
     191           0 :                 for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
     192           0 :                         if (sunptr->un_id == semid) {
     193           0 :                                 if (semnum == -1 || sunptr->un_num == semnum) {
     194           0 :                                         suptr->un_cnt--;
     195           0 :                                         if (i < suptr->un_cnt) {
     196           0 :                                                 suptr->un_ent[i] =
     197           0 :                                                   suptr->un_ent[suptr->un_cnt];
     198           0 :                                                 i--, sunptr--;
     199           0 :                                         }
     200             :                                 }
     201           0 :                                 if (semnum != -1)
     202             :                                         break;
     203             :                         }
     204             :                 }
     205           0 :                 if (suptr->un_cnt == 0) {
     206             :                         struct sem_undo *sutmp = suptr;
     207             : 
     208           0 :                         if (suptr == SLIST_FIRST(&semu_list))
     209           0 :                                 SLIST_REMOVE_HEAD(&semu_list, un_next);
     210             :                         else
     211           0 :                                 SLIST_REMOVE_AFTER(suprev, un_next);
     212           0 :                         suptr = SLIST_NEXT(suptr, un_next);
     213           0 :                         pool_put(&semu_pool, sutmp);
     214           0 :                         semutot--;
     215           0 :                 } else {
     216             :                         suprev = suptr;
     217           0 :                         suptr = SLIST_NEXT(suptr, un_next);
     218             :                 }
     219             :         }
     220           0 : }
     221             : 
     222             : int
     223           0 : sys___semctl(struct proc *p, void *v, register_t *retval)
     224             : {
     225             :         struct sys___semctl_args /* {
     226             :                 syscallarg(int) semid;
     227             :                 syscallarg(int) semnum;
     228             :                 syscallarg(int) cmd;
     229             :                 syscallarg(union semun *) arg;
     230           0 :         } */ *uap = v;
     231           0 :         union semun arg;
     232           0 :         int error = 0, cmd = SCARG(uap, cmd);
     233             : 
     234           0 :         switch (cmd) {
     235             :         case IPC_SET:
     236             :         case IPC_STAT:
     237             :         case GETALL:
     238             :         case SETVAL:
     239             :         case SETALL:
     240           0 :                 error = copyin(SCARG(uap, arg), &arg, sizeof(arg));
     241           0 :                 break;
     242             :         }
     243           0 :         if (error == 0) {
     244           0 :                 error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum),
     245             :                     cmd, &arg, retval, copyin, copyout);
     246           0 :         }
     247           0 :         return (error);
     248           0 : }
     249             : 
     250             : int
     251           0 : semctl1(struct proc *p, int semid, int semnum, int cmd, union semun *arg,
     252             :     register_t *retval, int (*ds_copyin)(const void *, void *, size_t),
     253             :     int (*ds_copyout)(const void *, void *, size_t))
     254             : {
     255           0 :         struct ucred *cred = p->p_ucred;
     256             :         int i, ix, error = 0;
     257           0 :         struct semid_ds sbuf;
     258             :         struct semid_ds *semaptr;
     259             :         unsigned short *semval = NULL;
     260             : 
     261             :         DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg));
     262             : 
     263           0 :         ix = IPCID_TO_IX(semid);
     264           0 :         if (ix < 0 || ix >= seminfo.semmni)
     265           0 :                 return (EINVAL);
     266             : 
     267           0 :         if ((semaptr = sema[ix]) == NULL ||
     268           0 :             semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
     269           0 :                 return (EINVAL);
     270             : 
     271           0 :         switch (cmd) {
     272             :         case IPC_RMID:
     273           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
     274           0 :                         return (error);
     275           0 :                 semaptr->sem_perm.cuid = cred->cr_uid;
     276           0 :                 semaptr->sem_perm.uid = cred->cr_uid;
     277           0 :                 semtot -= semaptr->sem_nsems;
     278           0 :                 free(semaptr->sem_base, M_SEM, 0);
     279           0 :                 pool_put(&sema_pool, semaptr);
     280           0 :                 sema[ix] = NULL;
     281           0 :                 semundo_clear(ix, -1);
     282           0 :                 wakeup(&sema[ix]);
     283           0 :                 break;
     284             : 
     285             :         case IPC_SET:
     286           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
     287           0 :                         return (error);
     288           0 :                 if ((error = ds_copyin(arg->buf, &sbuf, sizeof(sbuf))) != 0)
     289           0 :                         return (error);
     290           0 :                 semaptr->sem_perm.uid = sbuf.sem_perm.uid;
     291           0 :                 semaptr->sem_perm.gid = sbuf.sem_perm.gid;
     292           0 :                 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
     293           0 :                     (sbuf.sem_perm.mode & 0777);
     294           0 :                 semaptr->sem_ctime = time_second;
     295           0 :                 break;
     296             : 
     297             :         case IPC_STAT:
     298           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
     299           0 :                         return (error);
     300           0 :                 error = ds_copyout(semaptr, arg->buf, sizeof(struct semid_ds));
     301           0 :                 break;
     302             : 
     303             :         case GETNCNT:
     304           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
     305           0 :                         return (error);
     306           0 :                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
     307           0 :                         return (EINVAL);
     308           0 :                 *retval = semaptr->sem_base[semnum].semncnt;
     309           0 :                 break;
     310             : 
     311             :         case GETPID:
     312           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
     313           0 :                         return (error);
     314           0 :                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
     315           0 :                         return (EINVAL);
     316           0 :                 *retval = semaptr->sem_base[semnum].sempid;
     317           0 :                 break;
     318             : 
     319             :         case GETVAL:
     320           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
     321           0 :                         return (error);
     322           0 :                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
     323           0 :                         return (EINVAL);
     324           0 :                 *retval = semaptr->sem_base[semnum].semval;
     325           0 :                 break;
     326             : 
     327             :         case GETALL:
     328           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
     329           0 :                         return (error);
     330           0 :                 for (i = 0; i < semaptr->sem_nsems; i++) {
     331           0 :                         error = ds_copyout(&semaptr->sem_base[i].semval,
     332           0 :                             &arg->array[i], sizeof(arg->array[0]));
     333           0 :                         if (error != 0)
     334             :                                 break;
     335             :                 }
     336             :                 break;
     337             : 
     338             :         case GETZCNT:
     339           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
     340           0 :                         return (error);
     341           0 :                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
     342           0 :                         return (EINVAL);
     343           0 :                 *retval = semaptr->sem_base[semnum].semzcnt;
     344           0 :                 break;
     345             : 
     346             :         case SETVAL:
     347           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
     348           0 :                         return (error);
     349           0 :                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
     350           0 :                         return (EINVAL);
     351           0 :                 if (arg->val > seminfo.semvmx)
     352           0 :                         return (ERANGE);
     353           0 :                 semaptr->sem_base[semnum].semval = arg->val;
     354           0 :                 semundo_clear(ix, semnum);
     355           0 :                 wakeup(&sema[ix]);
     356           0 :                 break;
     357             : 
     358             :         case SETALL:
     359           0 :                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
     360           0 :                         return (error);
     361           0 :                 semval = mallocarray(semaptr->sem_nsems, sizeof(arg->array[0]),
     362             :                     M_TEMP, M_WAITOK);
     363           0 :                 for (i = 0; i < semaptr->sem_nsems; i++) {
     364           0 :                         error = ds_copyin(&arg->array[i], &semval[i],
     365             :                             sizeof(arg->array[0]));
     366           0 :                         if (error != 0)
     367             :                                 goto error;
     368           0 :                         if (semval[i] > seminfo.semvmx) {
     369             :                                 error = ERANGE;
     370           0 :                                 goto error;
     371             :                         }
     372             :                 }
     373           0 :                 for (i = 0; i < semaptr->sem_nsems; i++)
     374           0 :                         semaptr->sem_base[i].semval = semval[i];
     375           0 :                 semundo_clear(ix, -1);
     376           0 :                 wakeup(&sema[ix]);
     377           0 :                 break;
     378             : 
     379             :         default:
     380           0 :                 return (EINVAL);
     381             :         }
     382             : 
     383             : error:
     384           0 :         if (semval)
     385           0 :                 free(semval, M_TEMP,
     386           0 :                     semaptr->sem_nsems * sizeof(arg->array[0]));
     387             : 
     388           0 :         return (error);
     389           0 : }
     390             : 
     391             : int
     392           0 : sys_semget(struct proc *p, void *v, register_t *retval)
     393             : {
     394             :         struct sys_semget_args /* {
     395             :                 syscallarg(key_t) key;
     396             :                 syscallarg(int) nsems;
     397             :                 syscallarg(int) semflg;
     398           0 :         } */ *uap = v;
     399             :         int semid, error;
     400           0 :         int key = SCARG(uap, key);
     401           0 :         int nsems = SCARG(uap, nsems);
     402           0 :         int semflg = SCARG(uap, semflg);
     403             :         struct semid_ds *semaptr, *semaptr_new = NULL;
     404           0 :         struct ucred *cred = p->p_ucred;
     405             : 
     406             :         DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
     407             : 
     408             :         /*
     409             :          * Preallocate space for the new semaphore.  If we are going
     410             :          * to sleep, we want to sleep now to eliminate any race
     411             :          * condition in allocating a semaphore with a specific key.
     412             :          */
     413           0 :         if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
     414           0 :                 if (nsems <= 0 || nsems > seminfo.semmsl) {
     415             :                         DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
     416             :                             seminfo.semmsl));
     417           0 :                         return (EINVAL);
     418             :                 }
     419           0 :                 if (nsems > seminfo.semmns - semtot) {
     420             :                         DPRINTF(("not enough semaphores left (need %d, got %d)\n",
     421             :                             nsems, seminfo.semmns - semtot));
     422           0 :                         return (ENOSPC);
     423             :                 }
     424           0 :                 semaptr_new = pool_get(&sema_pool, PR_WAITOK);
     425           0 :                 semaptr_new->sem_base = mallocarray(nsems, sizeof(struct sem),
     426             :                     M_SEM, M_WAITOK|M_ZERO);
     427           0 :         }
     428             : 
     429           0 :         if (key != IPC_PRIVATE) {
     430           0 :                 for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
     431           0 :                         if ((semaptr = sema[semid]) != NULL &&
     432           0 :                             semaptr->sem_perm.key == key) {
     433             :                                 DPRINTF(("found public key\n"));
     434           0 :                                 if ((error = ipcperm(cred, &semaptr->sem_perm,
     435           0 :                                     semflg & 0700)))
     436             :                                         goto error;
     437           0 :                                 if (nsems > 0 && semaptr->sem_nsems < nsems) {
     438             :                                         DPRINTF(("too small\n"));
     439             :                                         error = EINVAL;
     440           0 :                                         goto error;
     441             :                                 }
     442           0 :                                 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
     443             :                                         DPRINTF(("not exclusive\n"));
     444             :                                         error = EEXIST;
     445           0 :                                         goto error;
     446             :                                 }
     447           0 :                                 if (semaptr_new != NULL) {
     448           0 :                                         free(semaptr_new->sem_base, M_SEM,
     449           0 :                                             nsems * sizeof(struct sem));
     450           0 :                                         pool_put(&sema_pool, semaptr_new);
     451           0 :                                 }
     452             :                                 goto found;
     453             :                         }
     454             :                 }
     455             :         }
     456             : 
     457             :         DPRINTF(("need to allocate the semid_ds\n"));
     458           0 :         if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
     459           0 :                 for (semid = 0; semid < seminfo.semmni; semid++) {
     460           0 :                         if ((semaptr = sema[semid]) == NULL)
     461             :                                 break;
     462             :                 }
     463           0 :                 if (semid == seminfo.semmni) {
     464             :                         DPRINTF(("no more semid_ds's available\n"));
     465             :                         error = ENOSPC;
     466           0 :                         goto error;
     467             :                 }
     468             :                 DPRINTF(("semid %d is available\n", semid));
     469           0 :                 semaptr_new->sem_perm.key = key;
     470           0 :                 semaptr_new->sem_perm.cuid = cred->cr_uid;
     471           0 :                 semaptr_new->sem_perm.uid = cred->cr_uid;
     472           0 :                 semaptr_new->sem_perm.cgid = cred->cr_gid;
     473           0 :                 semaptr_new->sem_perm.gid = cred->cr_gid;
     474           0 :                 semaptr_new->sem_perm.mode = (semflg & 0777);
     475           0 :                 semaptr_new->sem_perm.seq = semseqs[semid] =
     476           0 :                     (semseqs[semid] + 1) & 0x7fff;
     477           0 :                 semaptr_new->sem_nsems = nsems;
     478           0 :                 semaptr_new->sem_otime = 0;
     479           0 :                 semaptr_new->sem_ctime = time_second;
     480           0 :                 sema[semid] = semaptr_new;
     481           0 :                 semtot += nsems;
     482             :         } else {
     483             :                 DPRINTF(("didn't find it and wasn't asked to create it\n"));
     484           0 :                 return (ENOENT);
     485             :         }
     486             : 
     487             : found:
     488           0 :         *retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
     489           0 :         return (0);
     490             : error:
     491           0 :         if (semaptr_new != NULL) {
     492           0 :                 free(semaptr_new->sem_base, M_SEM, nsems * sizeof(struct sem));
     493           0 :                 pool_put(&sema_pool, semaptr_new);
     494           0 :         }
     495           0 :         return (error);
     496           0 : }
     497             : 
     498             : int
     499           0 : sys_semop(struct proc *p, void *v, register_t *retval)
     500             : {
     501             :         struct sys_semop_args /* {
     502             :                 syscallarg(int) semid;
     503             :                 syscallarg(struct sembuf *) sops;
     504             :                 syscallarg(size_t) nsops;
     505           0 :         } */ *uap = v;
     506             : #define NSOPS   8
     507           0 :         struct sembuf sopbuf[NSOPS];
     508           0 :         int semid = SCARG(uap, semid);
     509           0 :         size_t nsops = SCARG(uap, nsops);
     510             :         struct sembuf *sops;
     511             :         struct semid_ds *semaptr;
     512             :         struct sembuf *sopptr = NULL;
     513             :         struct sem *semptr = NULL;
     514           0 :         struct sem_undo *suptr = NULL;
     515           0 :         struct ucred *cred = p->p_ucred;
     516             :         size_t i, j;
     517             :         int do_wakeup, do_undos, error;
     518             : 
     519             :         DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
     520             :             (u_long)nsops));
     521             : 
     522           0 :         semid = IPCID_TO_IX(semid);     /* Convert back to zero origin */
     523             : 
     524           0 :         if (semid < 0 || semid >= seminfo.semmni)
     525           0 :                 return (EINVAL);
     526             : 
     527           0 :         if ((semaptr = sema[semid]) == NULL ||
     528           0 :             semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
     529           0 :                 return (EINVAL);
     530             : 
     531           0 :         if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
     532             :                 DPRINTF(("error = %d from ipaccess\n", error));
     533           0 :                 return (error);
     534             :         }
     535             : 
     536           0 :         if (nsops == 0) {
     537           0 :                 *retval = 0;
     538           0 :                 return (0);
     539           0 :         } else if (nsops > (size_t)seminfo.semopm) {
     540             :                 DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
     541             :                     (u_long)nsops));
     542           0 :                 return (E2BIG);
     543             :         }
     544             : 
     545           0 :         if (nsops <= NSOPS)
     546           0 :                 sops = sopbuf;
     547             :         else
     548           0 :                 sops = mallocarray(nsops, sizeof(struct sembuf), M_SEM, M_WAITOK);
     549           0 :         error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
     550           0 :         if (error != 0) {
     551             :                 DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
     552             :                     SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
     553             :                 goto done2;
     554             :         }
     555             : 
     556             :         /* 
     557             :          * Loop trying to satisfy the vector of requests.
     558             :          * If we reach a point where we must wait, any requests already
     559             :          * performed are rolled back and we go to sleep until some other
     560             :          * process wakes us up.  At this point, we start all over again.
     561             :          *
     562             :          * This ensures that from the perspective of other tasks, a set
     563             :          * of requests is atomic (never partially satisfied).
     564             :          */
     565             :         do_undos = 0;
     566             : 
     567           0 :         for (;;) {
     568             :                 do_wakeup = 0;
     569             : 
     570           0 :                 for (i = 0; i < nsops; i++) {
     571           0 :                         sopptr = &sops[i];
     572             : 
     573           0 :                         if (sopptr->sem_num >= semaptr->sem_nsems) {
     574             :                                 error = EFBIG;
     575           0 :                                 goto done2;
     576             :                         }
     577             : 
     578           0 :                         semptr = &semaptr->sem_base[sopptr->sem_num];
     579             : 
     580             :                         DPRINTF(("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
     581             :                             semaptr, semaptr->sem_base, semptr,
     582             :                             sopptr->sem_num, semptr->semval, sopptr->sem_op,
     583             :                             (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
     584             : 
     585           0 :                         if (sopptr->sem_op < 0) {
     586           0 :                                 if ((int)(semptr->semval +
     587           0 :                                           sopptr->sem_op) < 0) {
     588             :                                         DPRINTF(("semop:  can't do it now\n"));
     589             :                                         break;
     590             :                                 } else {
     591           0 :                                         semptr->semval += sopptr->sem_op;
     592           0 :                                         if (semptr->semval == 0 &&
     593           0 :                                             semptr->semzcnt > 0)
     594           0 :                                                 do_wakeup = 1;
     595             :                                 }
     596           0 :                                 if (sopptr->sem_flg & SEM_UNDO)
     597           0 :                                         do_undos++;
     598           0 :                         } else if (sopptr->sem_op == 0) {
     599           0 :                                 if (semptr->semval > 0) {
     600             :                                         DPRINTF(("semop:  not zero now\n"));
     601             :                                         break;
     602             :                                 }
     603             :                         } else {
     604           0 :                                 if (semptr->semncnt > 0)
     605           0 :                                         do_wakeup = 1;
     606           0 :                                 semptr->semval += sopptr->sem_op;
     607           0 :                                 if (sopptr->sem_flg & SEM_UNDO)
     608           0 :                                         do_undos++;
     609             :                         }
     610             :                 }
     611             : 
     612             :                 /*
     613             :                  * Did we get through the entire vector and can we undo it?
     614             :                  */
     615           0 :                 if (i >= nsops && do_undos <= SEMUME)
     616             :                         goto done;
     617             : 
     618             :                 /*
     619             :                  * No ... rollback anything that we've already done
     620             :                  */
     621             :                 DPRINTF(("semop:  rollback 0 through %d\n", i - 1));
     622           0 :                 for (j = 0; j < i; j++)
     623           0 :                         semaptr->sem_base[sops[j].sem_num].semval -=
     624           0 :                             sops[j].sem_op;
     625             : 
     626             :                 /*
     627             :                  * Did we have too many SEM_UNDO's
     628             :                  */
     629           0 :                 if (do_undos > SEMUME) {
     630             :                         error = ENOSPC;
     631           0 :                         goto done2;
     632             :                 }
     633             : 
     634             :                 /*
     635             :                  * If the request that we couldn't satisfy has the
     636             :                  * NOWAIT flag set then return with EAGAIN.
     637             :                  */
     638           0 :                 if (sopptr->sem_flg & IPC_NOWAIT) {
     639             :                         error = EAGAIN;
     640           0 :                         goto done2;
     641             :                 }
     642             : 
     643           0 :                 if (sopptr->sem_op == 0)
     644           0 :                         semptr->semzcnt++;
     645             :                 else
     646           0 :                         semptr->semncnt++;
     647             : 
     648             :                 DPRINTF(("semop:  good night!\n"));
     649           0 :                 error = tsleep(&sema[semid], PLOCK | PCATCH,
     650             :                     "semwait", 0);
     651             :                 DPRINTF(("semop:  good morning (error=%d)!\n", error));
     652             : 
     653           0 :                 suptr = NULL;   /* sem_undo may have been reallocated */
     654             : 
     655             :                 /*
     656             :                  * Make sure that the semaphore still exists
     657             :                  */
     658           0 :                 if (sema[semid] == NULL ||
     659           0 :                     semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
     660             :                         error = EIDRM;
     661           0 :                         goto done2;
     662             :                 }
     663             : 
     664             :                 /*
     665             :                  * The semaphore is still alive.  Readjust the count of
     666             :                  * waiting processes.
     667             :                  */
     668           0 :                 if (sopptr->sem_op == 0)
     669           0 :                         semptr->semzcnt--;
     670             :                 else
     671           0 :                         semptr->semncnt--;
     672             : 
     673             :                 /*
     674             :                  * Is it really morning, or was our sleep interrupted?
     675             :                  * (Delayed check of tsleep() return code because we
     676             :                  * need to decrement sem[nz]cnt either way.)
     677             :                  */
     678           0 :                 if (error != 0) {
     679             :                         error = EINTR;
     680           0 :                         goto done2;
     681             :                 }
     682             :                 DPRINTF(("semop:  good morning!\n"));
     683             :         }
     684             : 
     685             : done:
     686             :         /*
     687             :          * Process any SEM_UNDO requests.
     688             :          */
     689           0 :         if (do_undos) {
     690           0 :                 for (i = 0; i < nsops; i++) {
     691             :                         /*
     692             :                          * We only need to deal with SEM_UNDO's for non-zero
     693             :                          * op's.
     694             :                          */
     695             :                         int adjval;
     696             : 
     697           0 :                         if ((sops[i].sem_flg & SEM_UNDO) == 0)
     698           0 :                                 continue;
     699           0 :                         adjval = sops[i].sem_op;
     700           0 :                         if (adjval == 0)
     701           0 :                                 continue;
     702           0 :                         error = semundo_adjust(p, &suptr, semid,
     703           0 :                             sops[i].sem_num, -adjval);
     704           0 :                         if (error == 0)
     705           0 :                                 continue;
     706             : 
     707             :                         /*
     708             :                          * Uh-Oh!  We ran out of either sem_undo's or undo's.
     709             :                          * Rollback the adjustments to this point and then
     710             :                          * rollback the semaphore ups and down so we can return
     711             :                          * with an error with all structures restored.  We
     712             :                          * rollback the undo's in the exact reverse order that
     713             :                          * we applied them.  This guarantees that we won't run
     714             :                          * out of space as we roll things back out.
     715             :                          */
     716           0 :                         for (j = i; j > 0;) {
     717           0 :                                 j--;
     718           0 :                                 if ((sops[j].sem_flg & SEM_UNDO) == 0)
     719           0 :                                         continue;
     720           0 :                                 adjval = sops[j].sem_op;
     721           0 :                                 if (adjval == 0)
     722           0 :                                         continue;
     723           0 :                                 if (semundo_adjust(p, &suptr, semid,
     724           0 :                                     sops[j].sem_num, adjval) != 0)
     725           0 :                                         panic("semop - can't undo undos");
     726             :                         }
     727             : 
     728           0 :                         for (j = 0; j < nsops; j++)
     729           0 :                                 semaptr->sem_base[sops[j].sem_num].semval -=
     730           0 :                                     sops[j].sem_op;
     731             : 
     732             :                         DPRINTF(("error = %d from semundo_adjust\n", error));
     733           0 :                         goto done2;
     734             :                 } /* loop through the sops */
     735             :         } /* if (do_undos) */
     736             : 
     737             :         /* We're definitely done - set the sempid's */
     738           0 :         for (i = 0; i < nsops; i++) {
     739           0 :                 sopptr = &sops[i];
     740           0 :                 semptr = &semaptr->sem_base[sopptr->sem_num];
     741           0 :                 semptr->sempid = p->p_p->ps_pid;
     742             :         }
     743             : 
     744           0 :         semaptr->sem_otime = time_second;
     745             : 
     746             :         /* Do a wakeup if any semaphore was up'd. */
     747           0 :         if (do_wakeup) {
     748             :                 DPRINTF(("semop:  doing wakeup\n"));
     749           0 :                 wakeup(&sema[semid]);
     750             :                 DPRINTF(("semop:  back from wakeup\n"));
     751           0 :         }
     752             :         DPRINTF(("semop:  done\n"));
     753           0 :         *retval = 0;
     754             : done2:
     755           0 :         if (sops != sopbuf)
     756           0 :                 free(sops, M_SEM, nsops * sizeof(struct sembuf));
     757           0 :         return (error);
     758           0 : }
     759             : 
     760             : /*
     761             :  * Go through the undo structures for this process and apply the adjustments to
     762             :  * semaphores.
     763             :  */
     764             : void
     765           0 : semexit(struct process *pr)
     766             : {
     767             :         struct sem_undo *suptr;
     768             :         struct sem_undo **supptr;
     769             : 
     770             :         /*
     771             :          * Go through the chain of undo vectors looking for one associated with
     772             :          * this process.  Remember the pointer to the pointer to the element
     773             :          * to dequeue it later.
     774             :          */
     775             :         supptr = &SLIST_FIRST(&semu_list);
     776           0 :         SLIST_FOREACH(suptr, &semu_list, un_next) {
     777           0 :                 if (suptr->un_proc == pr)
     778             :                         break;
     779           0 :                 supptr = &SLIST_NEXT(suptr, un_next);
     780             :         }
     781             : 
     782             :         /*
     783             :          * If there is no undo vector, skip to the end.
     784             :          */
     785           0 :         if (suptr == NULL)
     786           0 :                 return;
     787             : 
     788             :         /*
     789             :          * We now have an undo vector for this process.
     790             :          */
     791             :         DPRINTF(("process @%p has undo structure with %d entries\n", pr,
     792             :             suptr->un_cnt));
     793             : 
     794             :         /*
     795             :          * If there are any active undo elements then process them.
     796             :          */
     797           0 :         if (suptr->un_cnt > 0) {
     798             :                 int ix;
     799             : 
     800           0 :                 for (ix = 0; ix < suptr->un_cnt; ix++) {
     801           0 :                         int semid = suptr->un_ent[ix].un_id;
     802           0 :                         int semnum = suptr->un_ent[ix].un_num;
     803           0 :                         int adjval = suptr->un_ent[ix].un_adjval;
     804             :                         struct semid_ds *semaptr;
     805             : 
     806           0 :                         if ((semaptr = sema[semid]) == NULL)
     807           0 :                                 panic("semexit - semid not allocated");
     808           0 :                         if (semnum >= semaptr->sem_nsems)
     809           0 :                                 panic("semexit - semnum out of range");
     810             : 
     811             :                         DPRINTF(("semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
     812             :                             suptr->un_proc, suptr->un_ent[ix].un_id,
     813             :                             suptr->un_ent[ix].un_num,
     814             :                             suptr->un_ent[ix].un_adjval,
     815             :                             semaptr->sem_base[semnum].semval));
     816             : 
     817           0 :                         if (adjval < 0 &&
     818           0 :                             semaptr->sem_base[semnum].semval < -adjval)
     819           0 :                                 semaptr->sem_base[semnum].semval = 0;
     820             :                         else
     821           0 :                                 semaptr->sem_base[semnum].semval += adjval;
     822             : 
     823           0 :                         wakeup(&sema[semid]);
     824             :                         DPRINTF(("semexit:  back from wakeup\n"));
     825             :                 }
     826           0 :         }
     827             : 
     828             :         /*
     829             :          * Deallocate the undo vector.
     830             :          */
     831             :         DPRINTF(("removing vector\n"));
     832           0 :         *supptr = SLIST_NEXT(suptr, un_next);
     833           0 :         pool_put(&semu_pool, suptr);
     834           0 :         semutot--;
     835           0 : }
     836             : 
     837             : /*
     838             :  * Userland access to struct seminfo.
     839             :  */
     840             : int
     841           0 : sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
     842             :         void *newp, size_t newlen)
     843             : {
     844           0 :         int error, val;
     845             :         struct semid_ds **sema_new;
     846             :         unsigned short *newseqs;
     847             : 
     848           0 :         if (namelen != 2) {
     849           0 :                 switch (name[0]) {
     850             :                 case KERN_SEMINFO_SEMMNI:
     851             :                 case KERN_SEMINFO_SEMMNS:
     852             :                 case KERN_SEMINFO_SEMMNU:
     853             :                 case KERN_SEMINFO_SEMMSL:
     854             :                 case KERN_SEMINFO_SEMOPM:
     855             :                 case KERN_SEMINFO_SEMUME:
     856             :                 case KERN_SEMINFO_SEMUSZ:
     857             :                 case KERN_SEMINFO_SEMVMX:
     858             :                 case KERN_SEMINFO_SEMAEM:
     859             :                         break;
     860             :                 default:
     861           0 :                         return (ENOTDIR);       /* overloaded */
     862             :                 }
     863             :         }
     864             : 
     865           0 :         switch (name[0]) {
     866             :         case KERN_SEMINFO_SEMMNI:
     867           0 :                 val = seminfo.semmni;
     868           0 :                 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
     869           0 :                     val == seminfo.semmni)
     870           0 :                         return (error);
     871             : 
     872           0 :                 if (val < seminfo.semmni || val > 0xffff)
     873           0 :                         return (EINVAL);
     874             : 
     875             :                 /* Expand semsegs and semseqs arrays */
     876           0 :                 sema_new = mallocarray(val, sizeof(struct semid_ds *),
     877             :                     M_SEM, M_WAITOK|M_ZERO);
     878           0 :                 memcpy(sema_new, sema,
     879             :                     seminfo.semmni * sizeof(struct semid_ds *));
     880           0 :                 newseqs = mallocarray(val, sizeof(unsigned short), M_SEM,
     881             :                     M_WAITOK|M_ZERO);
     882           0 :                 memcpy(newseqs, semseqs,
     883             :                     seminfo.semmni * sizeof(unsigned short));
     884           0 :                 free(sema, M_SEM, 0);
     885           0 :                 free(semseqs, M_SEM, 0);
     886           0 :                 sema = sema_new;
     887           0 :                 semseqs = newseqs;
     888           0 :                 seminfo.semmni = val;
     889           0 :                 return (0);
     890             :         case KERN_SEMINFO_SEMMNS:
     891           0 :                 val = seminfo.semmns;
     892           0 :                 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
     893           0 :                     val == seminfo.semmns)
     894           0 :                         return (error);
     895           0 :                 if (val < seminfo.semmns || val > 0xffff)
     896           0 :                         return (EINVAL);        /* can't decrease semmns */
     897           0 :                 seminfo.semmns = val;
     898           0 :                 return (0);
     899             :         case KERN_SEMINFO_SEMMNU:
     900           0 :                 val = seminfo.semmnu;
     901           0 :                 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
     902           0 :                     val == seminfo.semmnu)
     903           0 :                         return (error);
     904           0 :                 if (val < seminfo.semmnu)
     905           0 :                         return (EINVAL);        /* can't decrease semmnu */
     906           0 :                 seminfo.semmnu = val;
     907           0 :                 return (0);
     908             :         case KERN_SEMINFO_SEMMSL:
     909           0 :                 val = seminfo.semmsl;
     910           0 :                 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
     911           0 :                     val == seminfo.semmsl)
     912           0 :                         return (error);
     913           0 :                 if (val < seminfo.semmsl || val > 0xffff)
     914           0 :                         return (EINVAL);        /* can't decrease semmsl */
     915           0 :                 seminfo.semmsl = val;
     916           0 :                 return (0);
     917             :         case KERN_SEMINFO_SEMOPM:
     918           0 :                 val = seminfo.semopm;
     919           0 :                 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
     920           0 :                     val == seminfo.semopm)
     921           0 :                         return (error);
     922           0 :                 if (val <= 0)
     923           0 :                         return (EINVAL);        /* semopm must be >= 1 */
     924           0 :                 seminfo.semopm = val;
     925           0 :                 return (0);
     926             :         case KERN_SEMINFO_SEMUME:
     927           0 :                 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume));
     928             :         case KERN_SEMINFO_SEMUSZ:
     929           0 :                 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz));
     930             :         case KERN_SEMINFO_SEMVMX:
     931           0 :                 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx));
     932             :         case KERN_SEMINFO_SEMAEM:
     933           0 :                 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem));
     934             :         default:
     935           0 :                 return (EOPNOTSUPP);
     936             :         }
     937             :         /* NOTREACHED */
     938           0 : }

Generated by: LCOV version 1.13