static int sema_exist(int semid, struct semid_pool *semaptr) { /* Was it removed? */ if (semaptr->gen == -1 || semaptr->ds.sem_perm.seq != IPCID_TO_SEQ(semid)) return (0); return (1); }
struct shmid_ds * shm_find_segment_by_shmid(int shmid) { int segnum; struct shmid_ds *shmseg; segnum = IPCID_TO_IX(shmid); if (segnum < 0 || segnum >= shminfo.shmmni || (shmseg = shmsegs[segnum]) == NULL || shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) return (NULL); return (shmseg); }
struct shmid_ds * shm_find_segment_by_shmid(int shmid) { int segnum; struct shmid_ds *shmseg; segnum = IPCID_TO_IX(shmid); if (segnum < 0 || segnum >= shminfo.shmmni || (shmseg = shmsegs[segnum]) == NULL || shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) return (NULL); if ((shmseg->shm_perm.mode & (SHMSEG_REMOVED|SHMSEG_RMLINGER)) == SHMSEG_REMOVED) return (NULL); return (shmseg); }
static struct shmid_kernel * shm_find_segment_by_shmid(int shmid) { int segnum; struct shmid_kernel *shmseg; segnum = IPCID_TO_IX(shmid); if (segnum < 0 || segnum >= shminfo.shmmni) return NULL; shmseg = &shmsegs[segnum]; if ((shmseg->u.shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) != SHMSEG_ALLOCATED || shmseg->u.shm_perm._seq != IPCID_TO_SEQ(shmid)) return NULL; return shmseg; }
static struct shmid_ds * shm_find_segment_by_shmid(int shmid) { int segnum; struct shmid_ds *shmseg; segnum = IPCID_TO_IX(shmid); if (segnum < 0 || segnum >= shmalloced) { sysvd_print_err("segnum out of range\n"); return (NULL); } shmseg = &shmsegs[segnum]; if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) != SHMSEG_ALLOCATED || shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) { sysvd_print("segment most probably removed\n"); return (NULL); } return (shmseg); }
int msgctl1(struct lwp *l, int msqid, int cmd, struct msqid_ds *msqbuf) { kauth_cred_t cred = l->l_cred; struct msqid_ds *msqptr; kmsq_t *msq; int error = 0, ix; MSG_PRINTF(("call to msgctl1(%d, %d)\n", msqid, cmd)); ix = IPCID_TO_IX(msqid); mutex_enter(&msgmutex); if (ix < 0 || ix >= msginfo.msgmni) { MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", ix, msginfo.msgmni)); error = EINVAL; goto unlock; } msq = &msqs[ix]; msqptr = &msq->msq_u; if (msqptr->msg_qbytes == 0) { MSG_PRINTF(("no such msqid\n")); error = EINVAL; goto unlock; } if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqid)) { MSG_PRINTF(("wrong sequence number\n")); error = EINVAL; goto unlock; } switch (cmd) { case IPC_RMID: { struct __msg *msghdr; if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)) != 0) break; /* Free the message headers */ msghdr = msqptr->_msg_first; while (msghdr != NULL) { struct __msg *msghdr_tmp; /* Free the segments of each message */ msqptr->_msg_cbytes -= msghdr->msg_ts; msqptr->msg_qnum--; msghdr_tmp = msghdr; msghdr = msghdr->msg_next; msg_freehdr(msghdr_tmp); } KASSERT(msqptr->_msg_cbytes == 0); KASSERT(msqptr->msg_qnum == 0); /* Mark it as free */ msqptr->msg_qbytes = 0; cv_broadcast(&msq->msq_cv); } break; case IPC_SET: if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M))) break; if (msqbuf->msg_qbytes > msqptr->msg_qbytes && kauth_authorize_system(cred, KAUTH_SYSTEM_SYSVIPC, KAUTH_REQ_SYSTEM_SYSVIPC_MSGQ_OVERSIZE, KAUTH_ARG(msqbuf->msg_qbytes), KAUTH_ARG(msqptr->msg_qbytes), NULL) != 0) { error = EPERM; break; } if (msqbuf->msg_qbytes > msginfo.msgmnb) { MSG_PRINTF(("can't increase msg_qbytes beyond %d " "(truncating)\n", msginfo.msgmnb)); /* silently restrict qbytes to system limit */ msqbuf->msg_qbytes = msginfo.msgmnb; } if (msqbuf->msg_qbytes == 0) { MSG_PRINTF(("can't reduce msg_qbytes to 0\n")); error = EINVAL; /* XXX non-standard errno! */ break; } msqptr->msg_perm.uid = msqbuf->msg_perm.uid; msqptr->msg_perm.gid = msqbuf->msg_perm.gid; msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | (msqbuf->msg_perm.mode & 0777); msqptr->msg_qbytes = msqbuf->msg_qbytes; msqptr->msg_ctime = time_second; break; case IPC_STAT: if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { MSG_PRINTF(("requester doesn't have read access\n")); break; } memcpy(msqbuf, msqptr, sizeof(struct msqid_ds)); break; default: MSG_PRINTF(("invalid command %d\n", cmd)); error = EINVAL; break; } unlock: mutex_exit(&msgmutex); return (error); }
int sys_semop(struct lwp *l, const struct sys_semop_args *uap, register_t *retval) { /* { syscallarg(int) semid; syscallarg(struct sembuf *) sops; syscallarg(size_t) nsops; } */ struct proc *p = l->l_proc; int semid = SCARG(uap, semid), seq; size_t nsops = SCARG(uap, nsops); struct sembuf small_sops[SMALL_SOPS]; struct sembuf *sops; struct semid_ds *semaptr; struct sembuf *sopptr = NULL; struct __sem *semptr = NULL; struct sem_undo *suptr = NULL; kauth_cred_t cred = l->l_cred; int i, error; int do_wakeup, do_undos; SEM_PRINTF(("call to semop(%d, %p, %zd)\n", semid, SCARG(uap,sops), nsops)); if (__predict_false((p->p_flag & PK_SYSVSEM) == 0)) { mutex_enter(p->p_lock); p->p_flag |= PK_SYSVSEM; mutex_exit(p->p_lock); } restart: if (nsops <= SMALL_SOPS) { sops = small_sops; } else if (nsops <= seminfo.semopm) { sops = kmem_alloc(nsops * sizeof(*sops), KM_SLEEP); } else { SEM_PRINTF(("too many sops (max=%d, nsops=%zd)\n", seminfo.semopm, nsops)); return (E2BIG); } error = copyin(SCARG(uap, sops), sops, nsops * sizeof(sops[0])); if (error) { SEM_PRINTF(("error = %d from copyin(%p, %p, %zd)\n", error, SCARG(uap, sops), &sops, nsops * sizeof(sops[0]))); if (sops != small_sops) kmem_free(sops, nsops * sizeof(*sops)); return error; } mutex_enter(&semlock); /* In case of reallocation, we will wait for completion */ while (__predict_false(sem_realloc_state)) cv_wait(&sem_realloc_cv, &semlock); semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ if (semid < 0 || semid >= seminfo.semmni) { error = EINVAL; goto out; } semaptr = &sema[semid]; seq = IPCID_TO_SEQ(SCARG(uap, semid)); if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || semaptr->sem_perm._seq != seq) { error = EINVAL; goto out; } if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) { SEM_PRINTF(("error = %d from ipaccess\n", error)); goto out; } for (i = 0; i < nsops; i++) if (sops[i].sem_num >= semaptr->sem_nsems) { error = EFBIG; goto out; } /* * Loop trying to satisfy the vector of requests. * If we reach a point where we must wait, any requests already * performed are rolled back and we go to sleep until some other * process wakes us up. At this point, we start all over again. * * This ensures that from the perspective of other tasks, a set * of requests is atomic (never partially satisfied). */ do_undos = 0; for (;;) { do_wakeup = 0; for (i = 0; i < nsops; i++) { sopptr = &sops[i]; semptr = &semaptr->_sem_base[sopptr->sem_num]; SEM_PRINTF(("semop: semaptr=%p, sem_base=%p, " "semptr=%p, sem[%d]=%d : op=%d, flag=%s\n", semaptr, semaptr->_sem_base, semptr, sopptr->sem_num, semptr->semval, sopptr->sem_op, (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait")); if (sopptr->sem_op < 0) { if ((int)(semptr->semval + sopptr->sem_op) < 0) { SEM_PRINTF(("semop: " "can't do it now\n")); break; } else { semptr->semval += sopptr->sem_op; if (semptr->semval == 0 && semptr->semzcnt > 0) do_wakeup = 1; } if (sopptr->sem_flg & SEM_UNDO) do_undos = 1; } else if (sopptr->sem_op == 0) { if (semptr->semval > 0) { SEM_PRINTF(("semop: not zero now\n")); break; } } else { if (semptr->semncnt > 0) do_wakeup = 1; semptr->semval += sopptr->sem_op; if (sopptr->sem_flg & SEM_UNDO) do_undos = 1; } } /* * Did we get through the entire vector? */ if (i >= nsops) goto done; /* * No ... rollback anything that we've already done */ SEM_PRINTF(("semop: rollback 0 through %d\n", i - 1)); while (i-- > 0) semaptr->_sem_base[sops[i].sem_num].semval -= sops[i].sem_op; /* * If the request that we couldn't satisfy has the * NOWAIT flag set then return with EAGAIN. */ if (sopptr->sem_flg & IPC_NOWAIT) { error = EAGAIN; goto out; } if (sopptr->sem_op == 0) semptr->semzcnt++; else semptr->semncnt++; sem_waiters++; SEM_PRINTF(("semop: good night!\n")); error = cv_wait_sig(&semcv[semid], &semlock); SEM_PRINTF(("semop: good morning (error=%d)!\n", error)); sem_waiters--; /* Notify reallocator, if it is waiting */ cv_broadcast(&sem_realloc_cv); /* * Make sure that the semaphore still exists */ if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || semaptr->sem_perm._seq != seq) { error = EIDRM; goto out; } /* * The semaphore is still alive. Readjust the count of * waiting processes. */ semptr = &semaptr->_sem_base[sopptr->sem_num]; if (sopptr->sem_op == 0) semptr->semzcnt--; else semptr->semncnt--; /* In case of such state, restart the call */ if (sem_realloc_state) { mutex_exit(&semlock); goto restart; } /* Is it really morning, or was our sleep interrupted? */ if (error != 0) { error = EINTR; goto out; } SEM_PRINTF(("semop: good morning!\n")); } done: /* * Process any SEM_UNDO requests. */ if (do_undos) { for (i = 0; i < nsops; i++) { /* * We only need to deal with SEM_UNDO's for non-zero * op's. */ int adjval; if ((sops[i].sem_flg & SEM_UNDO) == 0) continue; adjval = sops[i].sem_op; if (adjval == 0) continue; error = semundo_adjust(p, &suptr, semid, sops[i].sem_num, -adjval); if (error == 0) continue; /* * Oh-Oh! We ran out of either sem_undo's or undo's. * Rollback the adjustments to this point and then * rollback the semaphore ups and down so we can return * with an error with all structures restored. We * rollback the undo's in the exact reverse order that * we applied them. This guarantees that we won't run * out of space as we roll things back out. */ while (i-- > 0) { if ((sops[i].sem_flg & SEM_UNDO) == 0) continue; adjval = sops[i].sem_op; if (adjval == 0) continue; if (semundo_adjust(p, &suptr, semid, sops[i].sem_num, adjval) != 0) panic("semop - can't undo undos"); } for (i = 0; i < nsops; i++) semaptr->_sem_base[sops[i].sem_num].semval -= sops[i].sem_op; SEM_PRINTF(("error = %d from semundo_adjust\n", error)); goto out; } /* loop through the sops */ } /* if (do_undos) */ /* We're definitely done - set the sempid's */ for (i = 0; i < nsops; i++) { sopptr = &sops[i]; semptr = &semaptr->_sem_base[sopptr->sem_num]; semptr->sempid = p->p_pid; } /* Update sem_otime */ semaptr->sem_otime = time_second; /* Do a wakeup if any semaphore was up'd. */ if (do_wakeup) { SEM_PRINTF(("semop: doing wakeup\n")); cv_broadcast(&semcv[semid]); SEM_PRINTF(("semop: back from wakeup\n")); } SEM_PRINTF(("semop: done\n")); *retval = 0; out: mutex_exit(&semlock); if (sops != small_sops) kmem_free(sops, nsops * sizeof(*sops)); return error; }
int semctl1(struct lwp *l, int semid, int semnum, int cmd, void *v, register_t *retval) { kauth_cred_t cred = l->l_cred; union __semun *arg = v; struct semid_ds *sembuf = v, *semaptr; int i, error, ix; SEM_PRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, v)); mutex_enter(&semlock); ix = IPCID_TO_IX(semid); if (ix < 0 || ix >= seminfo.semmni) { mutex_exit(&semlock); return (EINVAL); } semaptr = &sema[ix]; if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || semaptr->sem_perm._seq != IPCID_TO_SEQ(semid)) { mutex_exit(&semlock); return (EINVAL); } switch (cmd) { case IPC_RMID: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0) break; semaptr->sem_perm.cuid = kauth_cred_geteuid(cred); semaptr->sem_perm.uid = kauth_cred_geteuid(cred); semtot -= semaptr->sem_nsems; for (i = semaptr->_sem_base - sem; i < semtot; i++) sem[i] = sem[i + semaptr->sem_nsems]; for (i = 0; i < seminfo.semmni; i++) { if ((sema[i].sem_perm.mode & SEM_ALLOC) && sema[i]._sem_base > semaptr->_sem_base) sema[i]._sem_base -= semaptr->sem_nsems; } semaptr->sem_perm.mode = 0; semundo_clear(ix, -1); cv_broadcast(&semcv[ix]); break; case IPC_SET: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M))) break; KASSERT(sembuf != NULL); semaptr->sem_perm.uid = sembuf->sem_perm.uid; semaptr->sem_perm.gid = sembuf->sem_perm.gid; semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | (sembuf->sem_perm.mode & 0777); semaptr->sem_ctime = time_second; break; case IPC_STAT: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; KASSERT(sembuf != NULL); memcpy(sembuf, semaptr, sizeof(struct semid_ds)); sembuf->sem_perm.mode &= 0777; break; case GETNCNT: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } *retval = semaptr->_sem_base[semnum].semncnt; break; case GETPID: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } *retval = semaptr->_sem_base[semnum].sempid; break; case GETVAL: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } *retval = semaptr->_sem_base[semnum].semval; break; case GETALL: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; KASSERT(arg != NULL); for (i = 0; i < semaptr->sem_nsems; i++) { error = copyout(&semaptr->_sem_base[i].semval, &arg->array[i], sizeof(arg->array[i])); if (error != 0) break; } break; case GETZCNT: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } *retval = semaptr->_sem_base[semnum].semzcnt; break; case SETVAL: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } KASSERT(arg != NULL); if ((unsigned int)arg->val > seminfo.semvmx) { error = ERANGE; break; } semaptr->_sem_base[semnum].semval = arg->val; semundo_clear(ix, semnum); cv_broadcast(&semcv[ix]); break; case SETALL: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) break; KASSERT(arg != NULL); for (i = 0; i < semaptr->sem_nsems; i++) { unsigned short semval; error = copyin(&arg->array[i], &semval, sizeof(arg->array[i])); if (error != 0) break; if ((unsigned int)semval > seminfo.semvmx) { error = ERANGE; break; } semaptr->_sem_base[i].semval = semval; } semundo_clear(ix, -1); cv_broadcast(&semcv[ix]); break; default: error = EINVAL; break; } mutex_exit(&semlock); return (error); }
int sys_msgrcv(struct proc *p, void *v, register_t *retval) { struct sys_msgrcv_args /* { syscallarg(int) msqid; syscallarg(void *) msgp; syscallarg(size_t) msgsz; syscallarg(long) msgtyp; syscallarg(int) msgflg; } */ *uap = v; int msqid = SCARG(uap, msqid); char *user_msgp = SCARG(uap, msgp); size_t msgsz = SCARG(uap, msgsz); long msgtyp = SCARG(uap, msgtyp); int msgflg = SCARG(uap, msgflg); size_t len; struct ucred *cred = p->p_ucred; struct msqid_kernel *msqkptr; struct msg *msghdr; int eval; short next; DPRINTF(("call to msgrcv(%d, %p, %d, %ld, %d)\n", msqid, user_msgp, msgsz, msgtyp, msgflg)); msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni)); return (EINVAL); } msqkptr = &msqids[msqid]; if (msqkptr->u.msg_qbytes == 0) { DPRINTF(("no such message queue id\n")); return (EINVAL); } if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { DPRINTF(("wrong sequence number\n")); return (EINVAL); } if ((eval = ipcperm(cred, &msqkptr->u.msg_perm, IPC_R))) { DPRINTF(("requester doesn't have read access\n")); return (eval); } #if 0 /* cannot happen, msgsz is unsigned */ if (msgsz < 0) { DPRINTF(("msgsz < 0\n")); return (EINVAL); } #endif #ifdef MAC eval = mac_sysvmsq_check_msqrcv(cred, msqkptr); if (eval) return (eval); #endif msghdr = NULL; while (msghdr == NULL) { if (msgtyp == 0) { msghdr = msqkptr->u.msg_first; if (msghdr != NULL) { if (msgsz < msghdr->msg_ts && (msgflg & MSG_NOERROR) == 0) { DPRINTF(("first message on the queue " "is too big (want %d, got %d)\n", msgsz, msghdr->msg_ts)); return (E2BIG); } #ifdef MAC eval = mac_sysvmsq_check_msgrcv(cred, msghdr); if (eval) return (eval); #endif if (msqkptr->u.msg_first == msqkptr->u.msg_last) { msqkptr->u.msg_first = NULL; msqkptr->u.msg_last = NULL; } else { msqkptr->u.msg_first = msghdr->msg_next; #ifdef DIAGNOSTIC if (msqkptr->u.msg_first == NULL) panic("msg_first/last screwed up #1"); #endif } } } else { struct msg *previous; struct msg **prev; for (previous = NULL, prev = &msqkptr->u.msg_first; (msghdr = *prev) != NULL; previous = msghdr, prev = &msghdr->msg_next) { /* * Is this message's type an exact match or is * this message's type less than or equal to * the absolute value of a negative msgtyp? * Note that the second half of this test can * NEVER be true if msgtyp is positive since * msg_type is always positive! */ if (msgtyp == msghdr->msg_type || msghdr->msg_type <= -msgtyp) { DPRINTF(("found message type %d, " "requested %d\n", msghdr->msg_type, msgtyp)); if (msgsz < msghdr->msg_ts && (msgflg & MSG_NOERROR) == 0) { DPRINTF(("requested message on " "the queue is too big " "(want %d, got %d)\n", msgsz, msghdr->msg_ts)); return (E2BIG); } #ifdef MAC eval = mac_sysvmsq_check_msgrcv(cred, msghdr); if (eval) return (eval); #endif *prev = msghdr->msg_next; if (msghdr == msqkptr->u.msg_last) { if (previous == NULL) { #ifdef DIAGNOSTIC if (prev != &msqkptr->u.msg_first) panic("msg_first/last screwed up #2"); #endif msqkptr->u.msg_first = NULL; msqkptr->u.msg_last = NULL; } else { #ifdef DIAGNOSTIC if (prev == &msqkptr->u.msg_first) panic("msg_first/last screwed up #3"); #endif msqkptr->u.msg_last = previous; } } break; } } } /* * We've either extracted the msghdr for the appropriate * message or there isn't one. * If there is one then bail out of this loop. */ if (msghdr != NULL) break; /* * Hmph! No message found. Does the user want to wait? */ if ((msgflg & IPC_NOWAIT) != 0) { DPRINTF(("no appropriate message found (msgtyp=%d)\n", msgtyp)); return (ENOMSG); } /* * Wait for something to happen */ DPRINTF(("msgrcv: goodnight\n")); eval = tsleep(msqkptr, (PZERO - 4) | PCATCH, "msgwait", 0); DPRINTF(("msgrcv: good morning (eval=%d)\n", eval)); if (eval != 0) { DPRINTF(("msgsnd: interrupted system call\n")); return (EINTR); } /* * Make sure that the msq queue still exists */ if (msqkptr->u.msg_qbytes == 0 || msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { DPRINTF(("msqid deleted\n")); return (EIDRM); } } /* * Return the message to the user. * * First, do the bookkeeping (before we risk being interrupted). */ msqkptr->u.msg_cbytes -= msghdr->msg_ts; msqkptr->u.msg_qnum--; msqkptr->u.msg_lrpid = p->p_p->ps_mainproc->p_pid; msqkptr->u.msg_rtime = time_second; /* * Make msgsz the actual amount that we'll be returning. * Note that this effectively truncates the message if it is too long * (since msgsz is never increased). */ DPRINTF(("found a message, msgsz=%d, msg_ts=%d\n", msgsz, msghdr->msg_ts)); if (msgsz > msghdr->msg_ts) msgsz = msghdr->msg_ts; /* * Return the type to the user. */ eval = copyout(&msghdr->msg_type, user_msgp, sizeof(msghdr->msg_type)); if (eval != 0) { DPRINTF(("error (%d) copying out message type\n", eval)); msg_freehdr(msghdr); wakeup(msqkptr); return (eval); } user_msgp += sizeof(msghdr->msg_type); /* * Return the segments to the user */ next = msghdr->msg_spot; for (len = 0; len < msgsz; len += msginfo.msgssz) { size_t tlen; if (msgsz - len > msginfo.msgssz) tlen = msginfo.msgssz; else tlen = msgsz - len; #ifdef DIAGNOSTIC if (next <= -1) panic("next too low #3"); if (next >= msginfo.msgseg) panic("next out of range #3"); #endif eval = copyout(&msgpool[next * msginfo.msgssz], user_msgp, tlen); if (eval != 0) { DPRINTF(("error (%d) copying out message segment\n", eval)); msg_freehdr(msghdr); wakeup(msqkptr); return (eval); } user_msgp += tlen; next = msgmaps[next].next; } /* * Done, return the actual number of bytes copied out. */ msg_freehdr(msghdr); wakeup(msqkptr); *retval = msgsz; return (0); }
int sys_msgsnd(struct proc *p, void *v, register_t *retval) { struct sys_msgsnd_args /* { syscallarg(int) msqid; syscallarg(const void *) msgp; syscallarg(size_t) msgsz; syscallarg(int) msgflg; } */ *uap = v; int msqid = SCARG(uap, msqid); const char *user_msgp = SCARG(uap, msgp); size_t msgsz = SCARG(uap, msgsz); int msgflg = SCARG(uap, msgflg); int segs_needed, eval; struct ucred *cred = p->p_ucred; struct msqid_kernel *msqkptr; struct msg *msghdr; short next; DPRINTF(("call to msgsnd(%d, %p, %d, %d)\n", msqid, user_msgp, msgsz, msgflg)); msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni)); return (EINVAL); } msqkptr = &msqids[msqid]; if (msqkptr->u.msg_qbytes == 0) { DPRINTF(("no such message queue id\n")); return (EINVAL); } if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { DPRINTF(("wrong sequence number\n")); return (EINVAL); } if ((eval = ipcperm(cred, &msqkptr->u.msg_perm, IPC_W))) { DPRINTF(("requester doesn't have write access\n")); return (eval); } #ifdef MAC eval = mac_sysvmsq_check_msqsnd(cred, msqkptr); if (eval) return (eval); #endif segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, segs_needed)); for (;;) { int need_more_resources = 0; /* * check msgsz [cannot be negative since it is unsigned] * (inside this loop in case msg_qbytes changes while we sleep) */ if (msgsz > msqkptr->u.msg_qbytes) { DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n")); return (EINVAL); } if (msqkptr->u.msg_perm.mode & MSG_LOCKED) { DPRINTF(("msqid is locked\n")); need_more_resources = 1; } if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) { DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n")); need_more_resources = 1; } if (segs_needed > nfree_msgmaps) { DPRINTF(("segs_needed > nfree_msgmaps\n")); need_more_resources = 1; } if (free_msghdrs == NULL) { DPRINTF(("no more msghdrs\n")); need_more_resources = 1; } if (need_more_resources) { int we_own_it; if ((msgflg & IPC_NOWAIT) != 0) { DPRINTF(("need more resources but caller " "doesn't want to wait\n")); return (EAGAIN); } if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) { DPRINTF(("we don't own the msqid_ds\n")); we_own_it = 0; } else { /* Force later arrivals to wait for our request */ DPRINTF(("we own the msqid_ds\n")); msqkptr->u.msg_perm.mode |= MSG_LOCKED; we_own_it = 1; } DPRINTF(("goodnight\n")); eval = tsleep(msqkptr, (PZERO - 4) | PCATCH, "msgwait", 0); DPRINTF(("good morning, eval=%d\n", eval)); if (we_own_it) msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; if (eval != 0) { DPRINTF(("msgsnd: interrupted system call\n")); return (EINTR); } /* * Make sure that the msq queue still exists */ if (msqkptr->u.msg_qbytes == 0) { DPRINTF(("msqid deleted\n")); return (EIDRM); } } else { DPRINTF(("got all the resources that we need\n")); break; } } /* * We have the resources that we need. * Make sure! */ #ifdef DIAGNOSTIC if (msqkptr->u.msg_perm.mode & MSG_LOCKED) panic("msg_perm.mode & MSG_LOCKED"); if (segs_needed > nfree_msgmaps) panic("segs_needed > nfree_msgmaps"); if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) panic("msgsz + msg_cbytes > msg_qbytes"); if (free_msghdrs == NULL) panic("no more msghdrs"); #endif /* * Re-lock the msqid_ds in case we page-fault when copying in the * message */ #ifdef DIAGNOSTIC if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) panic("msqid_ds is already locked"); #endif msqkptr->u.msg_perm.mode |= MSG_LOCKED; /* * Allocate a message header */ msghdr = free_msghdrs; free_msghdrs = msghdr->msg_next; msghdr->msg_spot = -1; msghdr->msg_ts = msgsz; #ifdef MAC /* * XXXMAC: Should the mac_sysvmsq_check_msgmsq check follow here * immediately? Or, should it be checked just before the msg is * enqueued in the msgq (as it is done now)? */ mac_sysvmsg_create(cred, msqkptr, msghdr); #endif /* * Allocate space for the message */ while (segs_needed > 0) { #ifdef DIAGNOSTIC if (nfree_msgmaps <= 0) panic("not enough msgmaps"); if (free_msgmaps == -1) panic("nil free_msgmaps"); #endif next = free_msgmaps; #ifdef DIAGNOSTIC if (next <= -1) panic("next too low #1"); if (next >= msginfo.msgseg) panic("next out of range #1"); #endif DPRINTF(("allocating segment %d to message\n", next)); free_msgmaps = msgmaps[next].next; nfree_msgmaps--; msgmaps[next].next = msghdr->msg_spot; msghdr->msg_spot = next; segs_needed--; } /* * Copy in the message type */ if ((eval = copyin(user_msgp, &msghdr->msg_type, sizeof(msghdr->msg_type))) != 0) { DPRINTF(("error %d copying the message type\n", eval)); msg_freehdr(msghdr); msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; wakeup(msqkptr); return (eval); } user_msgp += sizeof(msghdr->msg_type); /* * Validate the message type */ if (msghdr->msg_type < 1) { msg_freehdr(msghdr); msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; wakeup(msqkptr); DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type)); return (EINVAL); } /* * Copy in the message body */ next = msghdr->msg_spot; while (msgsz > 0) { size_t tlen; if (msgsz > msginfo.msgssz) tlen = msginfo.msgssz; else tlen = msgsz; #ifdef DIAGNOSTIC if (next <= -1) panic("next too low #2"); if (next >= msginfo.msgseg) panic("next out of range #2"); #endif if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen)) != 0) { DPRINTF(("error %d copying in message segment\n", eval)); msg_freehdr(msghdr); msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; wakeup(msqkptr); return (eval); } msgsz -= tlen; user_msgp += tlen; next = msgmaps[next].next; } #ifdef DIAGNOSTIC if (next != -1) panic("didn't use all the msg segments"); #endif /* * We've got the message. Unlock the msqid_ds. */ msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; /* * Make sure that the msqid_ds is still allocated. */ if (msqkptr->u.msg_qbytes == 0) { msg_freehdr(msghdr); wakeup(msqkptr); return (EIDRM); } #ifdef MAC /* * Note: Since the task/thread allocates the msghdr and usually * primes it with its own MAC label, for a majority of policies, it * won't be necessary to check whether the msghdr has access * permissions to the msgq. The mac_sysvmsq_check_msqsnd check would * suffice in that case. However, this hook may be required where * individual policies derive a non-identical label for the msghdr * from the current thread label and may want to check the msghdr * enqueue permissions, along with read/write permissions to the * msgq. */ eval = mac_sysvmsq_check_msgmsq(cred, msghdr, msqkptr); if (eval) { msg_freehdr(msghdr); wakeup(msqkptr); return (eval); } #endif /* * Put the message into the queue */ if (msqkptr->u.msg_first == NULL) { msqkptr->u.msg_first = msghdr; msqkptr->u.msg_last = msghdr; } else { msqkptr->u.msg_last->msg_next = msghdr; msqkptr->u.msg_last = msghdr; } msqkptr->u.msg_last->msg_next = NULL; msqkptr->u.msg_cbytes += msghdr->msg_ts; msqkptr->u.msg_qnum++; msqkptr->u.msg_lspid = p->p_p->ps_mainproc->p_pid; msqkptr->u.msg_stime = time_second; wakeup(msqkptr); *retval = 0; return (0); }
int msgctl1(struct proc *p, int msqid, int cmd, caddr_t buf, int (*ds_copyin)(const void *, void *, size_t), int (*ds_copyout)(const void *, void *, size_t)) { struct ucred *cred = p->p_ucred; struct msqid_ds msqbuf; struct msqid_kernel *msqkptr; struct msg *msghdr; int ix, error = 0; DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid, cmd, buf)); ix = IPCID_TO_IX(msqid); if (ix < 0 || ix >= msginfo.msgmni) { DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", ix, msginfo.msgmni)); return (EINVAL); } msqkptr = &msqids[ix]; if (msqkptr->u.msg_qbytes == 0) { DPRINTF(("no such msqid\n")); return (EINVAL); } if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) { DPRINTF(("wrong sequence number\n")); return (EINVAL); } #ifdef MAC error = mac_sysvmsq_check_msqctl(cred, msqkptr, cmd); if (error) return (error); #endif switch (cmd) { case IPC_RMID: if ((error = ipcperm(cred, &msqkptr->u.msg_perm, IPC_M)) != 0) return (error); #ifdef MAC /* * Check that the process has MAC access permissions to * individual msghdrs. Note: We need to do this in a separate * loop because the actual loop alters the msq/msghdr info as * it progresses, and there is no going back if half the way * we discover that the process cannot free a certain msghdr. * The msq will get into an inconsistent state. */ for (msghdr = msqkptr->u.msg_first; msghdr != NULL; msghdr = msghdr->msg_next) { error = mac_sysvmsq_check_msgrmid(cred, msghdr); if (error) return (error); } #endif /* Free the message headers */ msghdr = msqkptr->u.msg_first; while (msghdr != NULL) { struct msg *msghdr_tmp; /* Free the segments of each message */ msqkptr->u.msg_cbytes -= msghdr->msg_ts; msqkptr->u.msg_qnum--; msghdr_tmp = msghdr; msghdr = msghdr->msg_next; msg_freehdr(msghdr_tmp); } #ifdef DIAGNOSTIC if (msqkptr->u.msg_cbytes != 0) panic("sys_msgctl: msg_cbytes is screwed up"); if (msqkptr->u.msg_qnum != 0) panic("sys_msgctl: msg_qnum is screwed up"); #endif #ifdef MAC mac_sysvmsq_cleanup(msqkptr); #endif msqkptr->u.msg_qbytes = 0; /* Mark it as free */ wakeup(msqkptr); break; case IPC_SET: if ((error = ipcperm(cred, &msqkptr->u.msg_perm, IPC_M))) return (error); if ((error = ds_copyin(buf, &msqbuf, sizeof(msqbuf))) != 0) return (error); if (msqbuf.msg_qbytes > msqkptr->u.msg_qbytes && cred->cr_uid != 0) return (EPERM); if (msqbuf.msg_qbytes > msginfo.msgmnb) { DPRINTF(("can't increase msg_qbytes beyond %d " "(truncating)\n", msginfo.msgmnb)); /* silently restrict qbytes to system limit */ msqbuf.msg_qbytes = msginfo.msgmnb; } if (msqbuf.msg_qbytes == 0) { DPRINTF(("can't reduce msg_qbytes to 0\n")); return (EINVAL); /* non-standard errno! */ } msqkptr->u.msg_perm.uid = msqbuf.msg_perm.uid; msqkptr->u.msg_perm.gid = msqbuf.msg_perm.gid; msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) | (msqbuf.msg_perm.mode & 0777); msqkptr->u.msg_qbytes = msqbuf.msg_qbytes; msqkptr->u.msg_ctime = time_second; break; case IPC_STAT: if ((error = ipcperm(cred, &msqkptr->u.msg_perm, IPC_R))) { DPRINTF(("requester doesn't have read access\n")); return (error); } error = ds_copyout(&msqkptr->u, buf, sizeof(struct msqid_ds)); break; default: DPRINTF(("invalid command %d\n", cmd)); return (EINVAL); } return (error); }
int msgsnd_nocancel(struct proc *p, struct msgsnd_nocancel_args *uap, int32_t *retval) { int msqid = uap->msqid; user_addr_t user_msgp = uap->msgp; size_t msgsz = (size_t)uap->msgsz; /* limit to 4G */ int msgflg = uap->msgflg; int segs_needed, eval; struct msqid_kernel *msqptr; struct msg *msghdr; short next; user_long_t msgtype; SYSV_MSG_SUBSYS_LOCK(); if (!msginit(0)) { eval = ENOMEM; goto msgsndout; } #ifdef MSG_DEBUG_OK printf("call to msgsnd(%d, 0x%qx, %ld, %d)\n", msqid, user_msgp, msgsz, msgflg); #endif AUDIT_ARG(svipc_id, msqid); msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif eval = EINVAL; goto msgsndout; } msqptr = &msqids[msqid]; if (msqptr->u.msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("no such message queue id\n"); #endif eval = EINVAL; goto msgsndout; } if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK printf("wrong sequence number\n"); #endif eval = EINVAL; goto msgsndout; } if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_W))) { #ifdef MSG_DEBUG_OK printf("requester doesn't have write access\n"); #endif goto msgsndout; } #if CONFIG_MACF eval = mac_sysvmsq_check_msqsnd(kauth_cred_get(), msqptr); if (eval) goto msgsndout; #endif segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; #ifdef MSG_DEBUG_OK printf("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, segs_needed); #endif /* * If we suffer resource starvation, we will sleep in this loop and * wait for more resources to become available. This is a loop to * ensure reacquisition of the mutex following any sleep, since there * are multiple resources under contention. */ for (;;) { void *blocking_resource = NULL; /* * Check that we have not had the maximum message size change * out from under us and render our message invalid while we * slept waiting for some resource. */ if (msgsz > msqptr->u.msg_qbytes) { #ifdef MSG_DEBUG_OK printf("msgsz > msqptr->msg_qbytes\n"); #endif eval = EINVAL; goto msgsndout; } /* * If the user_msqid_ds is already locked, we need to sleep on * the queue until it's unlocked. */ if (msqptr->u.msg_perm.mode & MSG_LOCKED) { #ifdef MSG_DEBUG_OK printf("msqid is locked\n"); #endif blocking_resource = msqptr; } /* * If our message plus the messages already in the queue would * cause us to exceed the maximum number of bytes wer are * permitted to queue, then block on the queue until it drains. */ if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes) { #ifdef MSG_DEBUG_OK printf("msgsz + msg_cbytes > msg_qbytes\n"); #endif blocking_resource = msqptr; } /* * Both message maps and message headers are protected by * sleeping on the address of the pointer to the list of free * message headers, since they are allocated and freed in * tandem. */ if (segs_needed > nfree_msgmaps) { #ifdef MSG_DEBUG_OK printf("segs_needed > nfree_msgmaps\n"); #endif blocking_resource = &free_msghdrs; } if (free_msghdrs == NULL) { #ifdef MSG_DEBUG_OK printf("no more msghdrs\n"); #endif blocking_resource = &free_msghdrs; } if (blocking_resource != NULL) { int we_own_it; if ((msgflg & IPC_NOWAIT) != 0) { #ifdef MSG_DEBUG_OK printf("need more resources but caller doesn't want to wait\n"); #endif eval = EAGAIN; goto msgsndout; } if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) { #ifdef MSG_DEBUG_OK printf("we don't own the user_msqid_ds\n"); #endif we_own_it = 0; } else { /* Force later arrivals to wait for our request */ #ifdef MSG_DEBUG_OK printf("we own the user_msqid_ds\n"); #endif msqptr->u.msg_perm.mode |= MSG_LOCKED; we_own_it = 1; } #ifdef MSG_DEBUG_OK printf("goodnight\n"); #endif eval = msleep(blocking_resource, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH, "msgwait", 0); #ifdef MSG_DEBUG_OK printf("good morning, eval=%d\n", eval); #endif if (we_own_it) msqptr->u.msg_perm.mode &= ~MSG_LOCKED; if (eval != 0) { #ifdef MSG_DEBUG_OK printf("msgsnd: interrupted system call\n"); #endif eval = EINTR; goto msgsndout; } /* * Make sure that the msq queue still exists */ if (msqptr->u.msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("msqid deleted\n"); #endif eval = EIDRM; goto msgsndout; } } else { #ifdef MSG_DEBUG_OK printf("got all the resources that we need\n"); #endif break; } } /* * We have the resources that we need. * Make sure! */ if (msqptr->u.msg_perm.mode & MSG_LOCKED) panic("msg_perm.mode & MSG_LOCKED"); if (segs_needed > nfree_msgmaps) panic("segs_needed > nfree_msgmaps"); if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes) panic("msgsz + msg_cbytes > msg_qbytes"); if (free_msghdrs == NULL) panic("no more msghdrs"); /* * Re-lock the user_msqid_ds in case we page-fault when copying in * the message */ if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) panic("user_msqid_ds is already locked"); msqptr->u.msg_perm.mode |= MSG_LOCKED; /* * Allocate a message header */ msghdr = free_msghdrs; free_msghdrs = msghdr->msg_next; msghdr->msg_spot = -1; msghdr->msg_ts = msgsz; #if CONFIG_MACF mac_sysvmsg_label_associate(kauth_cred_get(), msqptr, msghdr); #endif /* * Allocate space for the message */ while (segs_needed > 0) { if (nfree_msgmaps <= 0) panic("not enough msgmaps"); if (free_msgmaps == -1) panic("nil free_msgmaps"); next = free_msgmaps; if (next <= -1) panic("next too low #1"); if (next >= msginfo.msgseg) panic("next out of range #1"); #ifdef MSG_DEBUG_OK printf("allocating segment %d to message\n", next); #endif free_msgmaps = msgmaps[next].next; nfree_msgmaps--; msgmaps[next].next = msghdr->msg_spot; msghdr->msg_spot = next; segs_needed--; } /* * Copy in the message type. For a 64 bit process, this is 64 bits, * but we only ever use the low 32 bits, so the cast is OK. */ if (IS_64BIT_PROCESS(p)) { SYSV_MSG_SUBSYS_UNLOCK(); eval = copyin(user_msgp, &msgtype, sizeof(msgtype)); SYSV_MSG_SUBSYS_LOCK(); msghdr->msg_type = CAST_DOWN(long,msgtype); user_msgp = user_msgp + sizeof(msgtype); /* ptr math */ } else {
int msgctl(struct proc *p, struct msgctl_args *uap, int32_t *retval) { int msqid = uap->msqid; int cmd = uap->cmd; kauth_cred_t cred = kauth_cred_get(); int rval, eval; struct user_msqid_ds msqbuf; struct msqid_kernel *msqptr; SYSV_MSG_SUBSYS_LOCK(); if (!msginit(0)) { eval = ENOMEM; goto msgctlout; } #ifdef MSG_DEBUG_OK printf("call to msgctl(%d, %d, 0x%qx)\n", msqid, cmd, uap->buf); #endif AUDIT_ARG(svipc_cmd, cmd); AUDIT_ARG(svipc_id, msqid); msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif eval = EINVAL; goto msgctlout; } msqptr = &msqids[msqid]; if (msqptr->u.msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("no such msqid\n"); #endif eval = EINVAL; goto msgctlout; } if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK printf("wrong sequence number\n"); #endif eval = EINVAL; goto msgctlout; } #if CONFIG_MACF eval = mac_sysvmsq_check_msqctl(kauth_cred_get(), msqptr, cmd); if (eval) goto msgctlout; #endif eval = 0; rval = 0; switch (cmd) { case IPC_RMID: { struct msg *msghdr; if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M))) goto msgctlout; #if CONFIG_MACF /* * Check that the thread has MAC access permissions to * individual msghdrs. Note: We need to do this in a * separate loop because the actual loop alters the * msq/msghdr info as it progresses, and there is no going * back if half the way through we discover that the * thread cannot free a certain msghdr. The msq will get * into an inconsistent state. */ for (msghdr = msqptr->u.msg_first; msghdr != NULL; msghdr = msghdr->msg_next) { eval = mac_sysvmsq_check_msgrmid(kauth_cred_get(), msghdr); if (eval) goto msgctlout; } #endif /* Free the message headers */ msghdr = msqptr->u.msg_first; while (msghdr != NULL) { struct msg *msghdr_tmp; /* Free the segments of each message */ msqptr->u.msg_cbytes -= msghdr->msg_ts; msqptr->u.msg_qnum--; msghdr_tmp = msghdr; msghdr = msghdr->msg_next; msg_freehdr(msghdr_tmp); } if (msqptr->u.msg_cbytes != 0) panic("msg_cbytes is messed up"); if (msqptr->u.msg_qnum != 0) panic("msg_qnum is messed up"); msqptr->u.msg_qbytes = 0; /* Mark it as free */ #if CONFIG_MACF mac_sysvmsq_label_recycle(msqptr); #endif wakeup((caddr_t)msqptr); } break; case IPC_SET: if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M))) goto msgctlout; SYSV_MSG_SUBSYS_UNLOCK(); if (IS_64BIT_PROCESS(p)) { struct user64_msqid_ds tmpds; eval = copyin(uap->buf, &tmpds, sizeof(tmpds)); msqid_ds_user64tokernel(&tmpds, &msqbuf); } else { struct user32_msqid_ds tmpds; eval = copyin(uap->buf, &tmpds, sizeof(tmpds)); msqid_ds_user32tokernel(&tmpds, &msqbuf); } if (eval) return(eval); SYSV_MSG_SUBSYS_LOCK(); if (msqbuf.msg_qbytes > msqptr->u.msg_qbytes) { eval = suser(cred, &p->p_acflag); if (eval) goto msgctlout; } /* compare (msglen_t) value against restrict (int) value */ if (msqbuf.msg_qbytes > (user_msglen_t)msginfo.msgmnb) { #ifdef MSG_DEBUG_OK printf("can't increase msg_qbytes beyond %d (truncating)\n", msginfo.msgmnb); #endif msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ } if (msqbuf.msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("can't reduce msg_qbytes to 0\n"); #endif eval = EINVAL; goto msgctlout; } msqptr->u.msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ msqptr->u.msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ msqptr->u.msg_perm.mode = (msqptr->u.msg_perm.mode & ~0777) | (msqbuf.msg_perm.mode & 0777); msqptr->u.msg_qbytes = msqbuf.msg_qbytes; msqptr->u.msg_ctime = sysv_msgtime(); break; case IPC_STAT: if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_R))) { #ifdef MSG_DEBUG_OK printf("requester doesn't have read access\n"); #endif goto msgctlout; } SYSV_MSG_SUBSYS_UNLOCK(); if (IS_64BIT_PROCESS(p)) { struct user64_msqid_ds msqid_ds64 = {}; msqid_ds_kerneltouser64(&msqptr->u, &msqid_ds64); eval = copyout(&msqid_ds64, uap->buf, sizeof(msqid_ds64)); } else { struct user32_msqid_ds msqid_ds32 = {}; msqid_ds_kerneltouser32(&msqptr->u, &msqid_ds32); eval = copyout(&msqid_ds32, uap->buf, sizeof(msqid_ds32)); } SYSV_MSG_SUBSYS_LOCK(); break; default: #ifdef MSG_DEBUG_OK printf("invalid command %d\n", cmd); #endif eval = EINVAL; goto msgctlout; } if (eval == 0) *retval = rval; msgctlout: SYSV_MSG_SUBSYS_UNLOCK(); return(eval); }
int msgrcv1(struct lwp *l, int msqidr, char *user_msgp, size_t msgsz, long msgtyp, int msgflg, size_t typesz, copyout_t put_type, register_t *retval) { size_t len; kauth_cred_t cred = l->l_cred; struct msqid_ds *msqptr; struct __msg *msghdr; int error = 0, msqid; kmsq_t *msq; short next; MSG_PRINTF(("call to msgrcv(%d, %p, %lld, %ld, %d)\n", msqidr, user_msgp, (long long)msgsz, msgtyp, msgflg)); if ((ssize_t)msgsz < 0) return EINVAL; restart: msqid = IPCID_TO_IX(msqidr); mutex_enter(&msgmutex); /* In case of reallocation, we will wait for completion */ while (__predict_false(msg_realloc_state)) cv_wait(&msg_realloc_cv, &msgmutex); if (msqid < 0 || msqid >= msginfo.msgmni) { MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni)); error = EINVAL; goto unlock; } msq = &msqs[msqid]; msqptr = &msq->msq_u; if (msqptr->msg_qbytes == 0) { MSG_PRINTF(("no such message queue id\n")); error = EINVAL; goto unlock; } if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) { MSG_PRINTF(("wrong sequence number\n")); error = EINVAL; goto unlock; } if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { MSG_PRINTF(("requester doesn't have read access\n")); goto unlock; } msghdr = NULL; while (msghdr == NULL) { if (msgtyp == 0) { msghdr = msqptr->_msg_first; if (msghdr != NULL) { if (msgsz < msghdr->msg_ts && (msgflg & MSG_NOERROR) == 0) { MSG_PRINTF(("first msg on the queue " "is too big (want %lld, got %d)\n", (long long)msgsz, msghdr->msg_ts)); error = E2BIG; goto unlock; } if (msqptr->_msg_first == msqptr->_msg_last) { msqptr->_msg_first = NULL; msqptr->_msg_last = NULL; } else { msqptr->_msg_first = msghdr->msg_next; KASSERT(msqptr->_msg_first != NULL); } } } else { struct __msg *previous; struct __msg **prev; for (previous = NULL, prev = &msqptr->_msg_first; (msghdr = *prev) != NULL; previous = msghdr, prev = &msghdr->msg_next) { /* * Is this message's type an exact match or is * this message's type less than or equal to * the absolute value of a negative msgtyp? * Note that the second half of this test can * NEVER be true if msgtyp is positive since * msg_type is always positive! */ if (msgtyp != msghdr->msg_type && msghdr->msg_type > -msgtyp) continue; MSG_PRINTF(("found message type %ld, requested %ld\n", msghdr->msg_type, msgtyp)); if (msgsz < msghdr->msg_ts && (msgflg & MSG_NOERROR) == 0) { MSG_PRINTF(("requested message on the queue " "is too big (want %lld, got %d)\n", (long long)msgsz, msghdr->msg_ts)); error = E2BIG; goto unlock; } *prev = msghdr->msg_next; if (msghdr != msqptr->_msg_last) break; if (previous == NULL) { KASSERT(prev == &msqptr->_msg_first); msqptr->_msg_first = NULL; msqptr->_msg_last = NULL; } else { KASSERT(prev != &msqptr->_msg_first); msqptr->_msg_last = previous; } break; } } /* * We've either extracted the msghdr for the appropriate * message or there isn't one. * If there is one then bail out of this loop. */ if (msghdr != NULL) break; /* * Hmph! No message found. Does the user want to wait? */ if ((msgflg & IPC_NOWAIT) != 0) { MSG_PRINTF(("no appropriate message found (msgtyp=%ld)\n", msgtyp)); error = ENOMSG; goto unlock; } /* * Wait for something to happen */ msg_waiters++; MSG_PRINTF(("msgrcv: goodnight\n")); error = cv_wait_sig(&msq->msq_cv, &msgmutex); MSG_PRINTF(("msgrcv: good morning (error=%d)\n", error)); msg_waiters--; /* * In case of such state, notify reallocator and * restart the call. */ if (msg_realloc_state) { cv_broadcast(&msg_realloc_cv); mutex_exit(&msgmutex); goto restart; } if (error != 0) { MSG_PRINTF(("msgsnd: interrupted system call\n")); error = EINTR; goto unlock; } /* * Make sure that the msq queue still exists */ if (msqptr->msg_qbytes == 0 || msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) { MSG_PRINTF(("msqid deleted\n")); error = EIDRM; goto unlock; } } /* * Return the message to the user. * * First, do the bookkeeping (before we risk being interrupted). */ msqptr->_msg_cbytes -= msghdr->msg_ts; msqptr->msg_qnum--; msqptr->msg_lrpid = l->l_proc->p_pid; msqptr->msg_rtime = time_second; /* * Make msgsz the actual amount that we'll be returning. * Note that this effectively truncates the message if it is too long * (since msgsz is never increased). */ MSG_PRINTF(("found a message, msgsz=%lld, msg_ts=%d\n", (long long)msgsz, msghdr->msg_ts)); if (msgsz > msghdr->msg_ts) msgsz = msghdr->msg_ts; /* * Return the type to the user. */ mutex_exit(&msgmutex); error = (*put_type)(&msghdr->msg_type, user_msgp, typesz); mutex_enter(&msgmutex); if (error != 0) { MSG_PRINTF(("error (%d) copying out message type\n", error)); msg_freehdr(msghdr); cv_broadcast(&msq->msq_cv); goto unlock; } user_msgp += typesz; /* * Return the segments to the user */ next = msghdr->msg_spot; for (len = 0; len < msgsz; len += msginfo.msgssz) { size_t tlen; KASSERT(next > -1); KASSERT(next < msginfo.msgseg); if (msgsz - len > msginfo.msgssz) tlen = msginfo.msgssz; else tlen = msgsz - len; mutex_exit(&msgmutex); error = copyout(&msgpool[next * msginfo.msgssz], user_msgp, tlen); mutex_enter(&msgmutex); if (error != 0) { MSG_PRINTF(("error (%d) copying out message segment\n", error)); msg_freehdr(msghdr); cv_broadcast(&msq->msq_cv); goto unlock; } user_msgp += tlen; next = msgmaps[next].next; } /* * Done, return the actual number of bytes copied out. */ msg_freehdr(msghdr); cv_broadcast(&msq->msq_cv); *retval = msgsz; unlock: mutex_exit(&msgmutex); return error; }
int msgsnd1(struct lwp *l, int msqidr, const char *user_msgp, size_t msgsz, int msgflg, size_t typesz, copyin_t fetch_type) { int segs_needed, error = 0, msqid; kauth_cred_t cred = l->l_cred; struct msqid_ds *msqptr; struct __msg *msghdr; kmsq_t *msq; short next; MSG_PRINTF(("call to msgsnd(%d, %p, %lld, %d)\n", msqidr, user_msgp, (long long)msgsz, msgflg)); if ((ssize_t)msgsz < 0) return EINVAL; restart: msqid = IPCID_TO_IX(msqidr); mutex_enter(&msgmutex); /* In case of reallocation, we will wait for completion */ while (__predict_false(msg_realloc_state)) cv_wait(&msg_realloc_cv, &msgmutex); if (msqid < 0 || msqid >= msginfo.msgmni) { MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni)); error = EINVAL; goto unlock; } msq = &msqs[msqid]; msqptr = &msq->msq_u; if (msqptr->msg_qbytes == 0) { MSG_PRINTF(("no such message queue id\n")); error = EINVAL; goto unlock; } if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) { MSG_PRINTF(("wrong sequence number\n")); error = EINVAL; goto unlock; } if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_W))) { MSG_PRINTF(("requester doesn't have write access\n")); goto unlock; } segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; MSG_PRINTF(("msgsz=%lld, msgssz=%d, segs_needed=%d\n", (long long)msgsz, msginfo.msgssz, segs_needed)); for (;;) { int need_more_resources = 0; /* * check msgsz [cannot be negative since it is unsigned] * (inside this loop in case msg_qbytes changes while we sleep) */ if (msgsz > msqptr->msg_qbytes) { MSG_PRINTF(("msgsz > msqptr->msg_qbytes\n")); error = EINVAL; goto unlock; } if (msqptr->msg_perm.mode & MSG_LOCKED) { MSG_PRINTF(("msqid is locked\n")); need_more_resources = 1; } if (msgsz + msqptr->_msg_cbytes > msqptr->msg_qbytes) { MSG_PRINTF(("msgsz + msg_cbytes > msg_qbytes\n")); need_more_resources = 1; } if (segs_needed > nfree_msgmaps) { MSG_PRINTF(("segs_needed > nfree_msgmaps\n")); need_more_resources = 1; } if (free_msghdrs == NULL) { MSG_PRINTF(("no more msghdrs\n")); need_more_resources = 1; } if (need_more_resources) { int we_own_it; if ((msgflg & IPC_NOWAIT) != 0) { MSG_PRINTF(("need more resources but caller " "doesn't want to wait\n")); error = EAGAIN; goto unlock; } if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { MSG_PRINTF(("we don't own the msqid_ds\n")); we_own_it = 0; } else { /* Force later arrivals to wait for our request */ MSG_PRINTF(("we own the msqid_ds\n")); msqptr->msg_perm.mode |= MSG_LOCKED; we_own_it = 1; } msg_waiters++; MSG_PRINTF(("goodnight\n")); error = cv_wait_sig(&msq->msq_cv, &msgmutex); MSG_PRINTF(("good morning, error=%d\n", error)); msg_waiters--; if (we_own_it) msqptr->msg_perm.mode &= ~MSG_LOCKED; /* * In case of such state, notify reallocator and * restart the call. */ if (msg_realloc_state) { cv_broadcast(&msg_realloc_cv); mutex_exit(&msgmutex); goto restart; } if (error != 0) { MSG_PRINTF(("msgsnd: interrupted system " "call\n")); error = EINTR; goto unlock; } /* * Make sure that the msq queue still exists */ if (msqptr->msg_qbytes == 0) { MSG_PRINTF(("msqid deleted\n")); error = EIDRM; goto unlock; } } else { MSG_PRINTF(("got all the resources that we need\n")); break; } } /* * We have the resources that we need. * Make sure! */ KASSERT((msqptr->msg_perm.mode & MSG_LOCKED) == 0); KASSERT(segs_needed <= nfree_msgmaps); KASSERT(msgsz + msqptr->_msg_cbytes <= msqptr->msg_qbytes); KASSERT(free_msghdrs != NULL); /* * Re-lock the msqid_ds in case we page-fault when copying in the * message */ KASSERT((msqptr->msg_perm.mode & MSG_LOCKED) == 0); msqptr->msg_perm.mode |= MSG_LOCKED; /* * Allocate a message header */ msghdr = free_msghdrs; free_msghdrs = msghdr->msg_next; msghdr->msg_spot = -1; msghdr->msg_ts = msgsz; /* * Allocate space for the message */ while (segs_needed > 0) { KASSERT(nfree_msgmaps > 0); KASSERT(free_msgmaps != -1); KASSERT(free_msgmaps < msginfo.msgseg); next = free_msgmaps; MSG_PRINTF(("allocating segment %d to message\n", next)); free_msgmaps = msgmaps[next].next; nfree_msgmaps--; msgmaps[next].next = msghdr->msg_spot; msghdr->msg_spot = next; segs_needed--; } /* * Copy in the message type */ mutex_exit(&msgmutex); error = (*fetch_type)(user_msgp, &msghdr->msg_type, typesz); mutex_enter(&msgmutex); if (error != 0) { MSG_PRINTF(("error %d copying the message type\n", error)); msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; cv_broadcast(&msq->msq_cv); goto unlock; } user_msgp += typesz; /* * Validate the message type */ if (msghdr->msg_type < 1) { msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; cv_broadcast(&msq->msq_cv); MSG_PRINTF(("mtype (%ld) < 1\n", msghdr->msg_type)); error = EINVAL; goto unlock; } /* * Copy in the message body */ next = msghdr->msg_spot; while (msgsz > 0) { size_t tlen; KASSERT(next > -1); KASSERT(next < msginfo.msgseg); if (msgsz > msginfo.msgssz) tlen = msginfo.msgssz; else tlen = msgsz; mutex_exit(&msgmutex); error = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen); mutex_enter(&msgmutex); if (error != 0) { MSG_PRINTF(("error %d copying in message segment\n", error)); msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; cv_broadcast(&msq->msq_cv); goto unlock; } msgsz -= tlen; user_msgp += tlen; next = msgmaps[next].next; } KASSERT(next == -1); /* * We've got the message. Unlock the msqid_ds. */ msqptr->msg_perm.mode &= ~MSG_LOCKED; /* * Make sure that the msqid_ds is still allocated. */ if (msqptr->msg_qbytes == 0) { msg_freehdr(msghdr); cv_broadcast(&msq->msq_cv); error = EIDRM; goto unlock; } /* * Put the message into the queue */ if (msqptr->_msg_first == NULL) { msqptr->_msg_first = msghdr; msqptr->_msg_last = msghdr; } else { msqptr->_msg_last->msg_next = msghdr; msqptr->_msg_last = msghdr; } msqptr->_msg_last->msg_next = NULL; msqptr->_msg_cbytes += msghdr->msg_ts; msqptr->msg_qnum++; msqptr->msg_lspid = l->l_proc->p_pid; msqptr->msg_stime = time_second; cv_broadcast(&msq->msq_cv); unlock: mutex_exit(&msgmutex); return error; }
int semexit(int undoid) { struct sem_undo *suptr; struct sem *semptr; struct shmid_ds *undoseg; if (undoid < 0) { return (-1); } undoseg = shm_find_segment_by_shmid(undoid); /* The UNDO segment must be mapped by only one segment. */ if (undoseg->shm_nattch != 1) { sysvd_print_err("undo segment mapped by more" "than one process\n"); exit(-1); } suptr = (struct sem_undo *)map_seg(undoid); if (suptr == NULL) { sysvd_print_err("no %d undo segment found\n", undoid); return (-1); } /* No locking mechanism is required because only the * client and the daemon can access the UNDO segment. * At this moment the client is disconnected so only * the daemon can modify this segment. */ while (suptr->un_cnt) { struct semid_pool *semaptr; int semid; int semnum; int adjval; int ix; ix = suptr->un_cnt - 1; semid = suptr->un_ent[ix].un_id; semnum = suptr->un_ent[ix].un_num; adjval = suptr->un_ent[ix].un_adjval; semaptr = (struct semid_pool *)map_seg(semid); if (!semaptr) { return (-1); } /* Was it removed? */ if (semaptr->gen == -1 || semaptr->ds.sem_perm.seq != IPCID_TO_SEQ(semid) || (semaptr->ds.sem_perm.mode & SHMSEG_ALLOCATED) == 0) { --suptr->un_cnt; sysvd_print_err("semexit - semid not allocated\n"); continue; } if (semnum >= semaptr->ds.sem_nsems) { --suptr->un_cnt; sysvd_print_err("semexit - semnum out of range\n"); continue; } #ifdef SYSV_RWLOCK #ifdef SYSV_SEMS sysv_rwlock_rdlock(&semaptr->rwlock); #else sysv_rwlock_wrlock(&semaptr->rwlock); #endif //SYSV_SEMS #else sysv_mutex_lock(&semaptr->mutex); /* Nobody can remove the semaphore beteen the check and the * lock acquisition because it must first send a IPC_RMID * to me and I will process that after finishing this function. */ #endif //SYSV_RWLOCK semptr = &semaptr->ds.sem_base[semnum]; #ifdef SYSV_SEMS sysv_mutex_lock(&semptr->sem_mutex); #endif if (ix == suptr->un_cnt - 1 && semid == suptr->un_ent[ix].un_id && semnum == suptr->un_ent[ix].un_num && adjval == suptr->un_ent[ix].un_adjval) { --suptr->un_cnt; if (adjval < 0) { if (semptr->semval < -adjval) semptr->semval = 0; else semptr->semval += adjval; } else { semptr->semval += adjval; } /* TODO multithreaded daemon: * Check again if the semaphore was removed and do * not wake anyone if it was.*/ umtx_wakeup((int *)&semptr->semval, 0); } #ifdef SYSV_SEMS sysv_mutex_unlock(&semptr->sem_mutex); #endif #ifdef SYSV_RWLOCK sysv_rwlock_unlock(&semaptr->rwlock); #else sysv_mutex_unlock(&semaptr->mutex); #endif munmap_seg(semid, semaptr); } munmap_seg(undoid, suptr); return (0); }
/* * MPALMOSTSAFE */ int sys_msgctl(struct msgctl_args *uap) { struct thread *td = curthread; struct proc *p = td->td_proc; int msqid = uap->msqid; int cmd = uap->cmd; struct msqid_ds *user_msqptr = uap->buf; int rval, eval; struct msqid_ds msqbuf; struct msqid_ds *msqptr; #ifdef MSG_DEBUG_OK kprintf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr); #endif if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL) return (ENOSYS); get_mplock(); msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK kprintf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif eval = EINVAL; goto done; } msqptr = &msqids[msqid]; if (msqptr->msg_qbytes == 0) { #ifdef MSG_DEBUG_OK kprintf("no such msqid\n"); #endif eval = EINVAL; goto done; } if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK kprintf("wrong sequence number\n"); #endif eval = EINVAL; goto done; } rval = 0; switch (cmd) { case IPC_RMID: { struct msg *msghdr; if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)) != 0) break; /* Free the message headers */ msghdr = msqptr->msg_first; while (msghdr != NULL) { struct msg *msghdr_tmp; /* Free the segments of each message */ msqptr->msg_cbytes -= msghdr->msg_ts; msqptr->msg_qnum--; msghdr_tmp = msghdr; msghdr = msghdr->msg_next; msg_freehdr(msghdr_tmp); } if (msqptr->msg_cbytes != 0) panic("msg_cbytes is screwed up"); if (msqptr->msg_qnum != 0) panic("msg_qnum is screwed up"); msqptr->msg_qbytes = 0; /* Mark it as free */ wakeup((caddr_t)msqptr); } break; case IPC_SET: if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)) != 0) break; if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) break; if (msqbuf.msg_qbytes > msqptr->msg_qbytes) { eval = priv_check(td, PRIV_ROOT); if (eval) break; } if (msqbuf.msg_qbytes > msginfo.msgmnb) { #ifdef MSG_DEBUG_OK kprintf("can't increase msg_qbytes beyond %d (truncating)\n", msginfo.msgmnb); #endif msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ } if (msqbuf.msg_qbytes == 0) { #ifdef MSG_DEBUG_OK kprintf("can't reduce msg_qbytes to 0\n"); #endif eval = EINVAL; /* non-standard errno! */ break; } msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | (msqbuf.msg_perm.mode & 0777); msqptr->msg_qbytes = msqbuf.msg_qbytes; msqptr->msg_ctime = time_second; break; case IPC_STAT: if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) { #ifdef MSG_DEBUG_OK kprintf("requester doesn't have read access\n"); #endif eval = EINVAL; break; } eval = copyout(msqptr, user_msqptr, sizeof(struct msqid_ds)); break; default: #ifdef MSG_DEBUG_OK kprintf("invalid command %d\n", cmd); #endif eval = EINVAL; break; } done: rel_mplock(); if (eval == 0) uap->sysmsg_result = rval; return(eval); }
/* * MPALMOSTSAFE */ int sys_msgrcv(struct msgrcv_args *uap) { struct thread *td = curthread; int msqid = uap->msqid; void *user_msgp = uap->msgp; size_t msgsz = uap->msgsz; long msgtyp = uap->msgtyp; int msgflg = uap->msgflg; size_t len; struct msqid_ds *msqptr; struct msg *msghdr; int eval; short next; #ifdef MSG_DEBUG_OK kprintf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, msgsz, msgtyp, msgflg); #endif if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL) return (ENOSYS); get_mplock(); msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK kprintf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif eval = EINVAL; goto done; } msqptr = &msqids[msqid]; if (msqptr->msg_qbytes == 0) { #ifdef MSG_DEBUG_OK kprintf("no such message queue id\n"); #endif eval = EINVAL; goto done; } if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK kprintf("wrong sequence number\n"); #endif eval = EINVAL; goto done; } if ((eval = ipcperm(td->td_proc, &msqptr->msg_perm, IPC_R))) { #ifdef MSG_DEBUG_OK kprintf("requester doesn't have read access\n"); #endif goto done; } msghdr = NULL; while (msghdr == NULL) { if (msgtyp == 0) { msghdr = msqptr->msg_first; if (msghdr != NULL) { if (msgsz < msghdr->msg_ts && (msgflg & MSG_NOERROR) == 0) { #ifdef MSG_DEBUG_OK kprintf("first message on the queue is too big (want %d, got %d)\n", msgsz, msghdr->msg_ts); #endif eval = E2BIG; goto done; } if (msqptr->msg_first == msqptr->msg_last) { msqptr->msg_first = NULL; msqptr->msg_last = NULL; } else { msqptr->msg_first = msghdr->msg_next; if (msqptr->msg_first == NULL) panic("msg_first/last screwed up #1"); } } } else { struct msg *previous; struct msg **prev; previous = NULL; prev = &(msqptr->msg_first); while ((msghdr = *prev) != NULL) { /* * Is this message's type an exact match or is * this message's type less than or equal to * the absolute value of a negative msgtyp? * Note that the second half of this test can * NEVER be true if msgtyp is positive since * msg_type is always positive! */ if (msgtyp == msghdr->msg_type || msghdr->msg_type <= -msgtyp) { #ifdef MSG_DEBUG_OK kprintf("found message type %d, requested %d\n", msghdr->msg_type, msgtyp); #endif if (msgsz < msghdr->msg_ts && (msgflg & MSG_NOERROR) == 0) { #ifdef MSG_DEBUG_OK kprintf("requested message on the queue is too big (want %d, got %d)\n", msgsz, msghdr->msg_ts); #endif eval = E2BIG; goto done; } *prev = msghdr->msg_next; if (msghdr == msqptr->msg_last) { if (previous == NULL) { if (prev != &msqptr->msg_first) panic("msg_first/last screwed up #2"); msqptr->msg_first = NULL; msqptr->msg_last = NULL; } else { if (prev == &msqptr->msg_first) panic("msg_first/last screwed up #3"); msqptr->msg_last = previous; } } break; } previous = msghdr; prev = &(msghdr->msg_next); } } /* * We've either extracted the msghdr for the appropriate * message or there isn't one. * If there is one then bail out of this loop. */ if (msghdr != NULL) break; /* * Hmph! No message found. Does the user want to wait? */ if ((msgflg & IPC_NOWAIT) != 0) { #ifdef MSG_DEBUG_OK kprintf("no appropriate message found (msgtyp=%d)\n", msgtyp); #endif /* The SVID says to return ENOMSG. */ #ifdef ENOMSG eval = ENOMSG; #else /* Unfortunately, BSD doesn't define that code yet! */ eval = EAGAIN; #endif goto done; } /* * Wait for something to happen */ #ifdef MSG_DEBUG_OK kprintf("msgrcv: goodnight\n"); #endif eval = tsleep((caddr_t)msqptr, PCATCH, "msgwait", 0); #ifdef MSG_DEBUG_OK kprintf("msgrcv: good morning (eval=%d)\n", eval); #endif if (eval != 0) { #ifdef MSG_DEBUG_OK kprintf("msgsnd: interrupted system call\n"); #endif eval = EINTR; goto done; } /* * Make sure that the msq queue still exists */ if (msqptr->msg_qbytes == 0 || msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK kprintf("msqid deleted\n"); #endif eval = EIDRM; goto done; } } /* * Return the message to the user. * * First, do the bookkeeping (before we risk being interrupted). */ msqptr->msg_cbytes -= msghdr->msg_ts; msqptr->msg_qnum--; msqptr->msg_lrpid = td->td_proc->p_pid; msqptr->msg_rtime = time_second; /* * Make msgsz the actual amount that we'll be returning. * Note that this effectively truncates the message if it is too long * (since msgsz is never increased). */ #ifdef MSG_DEBUG_OK kprintf("found a message, msgsz=%d, msg_ts=%d\n", msgsz, msghdr->msg_ts); #endif if (msgsz > msghdr->msg_ts) msgsz = msghdr->msg_ts; /* * Return the type to the user. */ eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp, sizeof(msghdr->msg_type)); if (eval != 0) { #ifdef MSG_DEBUG_OK kprintf("error (%d) copying out message type\n", eval); #endif msg_freehdr(msghdr); wakeup((caddr_t)msqptr); goto done; } user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); /* * Return the segments to the user */ next = msghdr->msg_spot; for (len = 0; len < msgsz; len += msginfo.msgssz) { size_t tlen; if (msgsz - len > msginfo.msgssz) tlen = msginfo.msgssz; else tlen = msgsz - len; if (next <= -1) panic("next too low #3"); if (next >= msginfo.msgseg) panic("next out of range #3"); eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz], user_msgp, tlen); if (eval != 0) { #ifdef MSG_DEBUG_OK kprintf("error (%d) copying out message segment\n", eval); #endif msg_freehdr(msghdr); wakeup((caddr_t)msqptr); goto done; } user_msgp = (char *)user_msgp + tlen; next = msgmaps[next].next; } /* * Done, return the actual number of bytes copied out. */ msg_freehdr(msghdr); wakeup((caddr_t)msqptr); eval = 0; done: rel_mplock(); if (eval == 0) uap->sysmsg_result = msgsz; return(eval); }
/* * MPALMOSTSAFE */ int sys_msgsnd(struct msgsnd_args *uap) { struct thread *td = curthread; int msqid = uap->msqid; const void *user_msgp = uap->msgp; size_t msgsz = uap->msgsz; int msgflg = uap->msgflg; int segs_needed, eval; struct msqid_ds *msqptr; struct msg *msghdr; short next; #ifdef MSG_DEBUG_OK kprintf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, msgflg); #endif if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL) return (ENOSYS); get_mplock(); msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK kprintf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif eval = EINVAL; goto done; } msqptr = &msqids[msqid]; if (msqptr->msg_qbytes == 0) { #ifdef MSG_DEBUG_OK kprintf("no such message queue id\n"); #endif eval = EINVAL; goto done; } if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK kprintf("wrong sequence number\n"); #endif eval = EINVAL; goto done; } if ((eval = ipcperm(td->td_proc, &msqptr->msg_perm, IPC_W))) { #ifdef MSG_DEBUG_OK kprintf("requester doesn't have write access\n"); #endif eval = EINVAL; goto done; } segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; #ifdef MSG_DEBUG_OK kprintf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, segs_needed); #endif for (;;) { int need_more_resources = 0; /* * check msgsz * (inside this loop in case msg_qbytes changes while we sleep) */ if (msgsz > msqptr->msg_qbytes) { #ifdef MSG_DEBUG_OK kprintf("msgsz > msqptr->msg_qbytes\n"); #endif eval = EINVAL; goto done; } if (msqptr->msg_perm.mode & MSG_LOCKED) { #ifdef MSG_DEBUG_OK kprintf("msqid is locked\n"); #endif need_more_resources = 1; } if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { #ifdef MSG_DEBUG_OK kprintf("msgsz + msg_cbytes > msg_qbytes\n"); #endif need_more_resources = 1; } if (segs_needed > nfree_msgmaps) { #ifdef MSG_DEBUG_OK kprintf("segs_needed > nfree_msgmaps\n"); #endif need_more_resources = 1; } if (free_msghdrs == NULL) { #ifdef MSG_DEBUG_OK kprintf("no more msghdrs\n"); #endif need_more_resources = 1; } if (need_more_resources) { int we_own_it; if ((msgflg & IPC_NOWAIT) != 0) { #ifdef MSG_DEBUG_OK kprintf("need more resources but caller doesn't want to wait\n"); #endif eval = EAGAIN; goto done; } if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { #ifdef MSG_DEBUG_OK kprintf("we don't own the msqid_ds\n"); #endif we_own_it = 0; } else { /* Force later arrivals to wait for our request */ #ifdef MSG_DEBUG_OK kprintf("we own the msqid_ds\n"); #endif msqptr->msg_perm.mode |= MSG_LOCKED; we_own_it = 1; } #ifdef MSG_DEBUG_OK kprintf("goodnight\n"); #endif eval = tsleep((caddr_t)msqptr, PCATCH, "msgwait", 0); #ifdef MSG_DEBUG_OK kprintf("good morning, eval=%d\n", eval); #endif if (we_own_it) msqptr->msg_perm.mode &= ~MSG_LOCKED; if (eval != 0) { #ifdef MSG_DEBUG_OK kprintf("msgsnd: interrupted system call\n"); #endif eval = EINTR; goto done; } /* * Make sure that the msq queue still exists */ if (msqptr->msg_qbytes == 0) { #ifdef MSG_DEBUG_OK kprintf("msqid deleted\n"); #endif eval = EIDRM; goto done; } } else { #ifdef MSG_DEBUG_OK kprintf("got all the resources that we need\n"); #endif break; } } /* * We have the resources that we need. * Make sure! */ if (msqptr->msg_perm.mode & MSG_LOCKED) panic("msg_perm.mode & MSG_LOCKED"); if (segs_needed > nfree_msgmaps) panic("segs_needed > nfree_msgmaps"); if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) panic("msgsz + msg_cbytes > msg_qbytes"); if (free_msghdrs == NULL) panic("no more msghdrs"); /* * Re-lock the msqid_ds in case we page-fault when copying in the * message */ if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) panic("msqid_ds is already locked"); msqptr->msg_perm.mode |= MSG_LOCKED; /* * Allocate a message header */ msghdr = free_msghdrs; free_msghdrs = msghdr->msg_next; msghdr->msg_spot = -1; msghdr->msg_ts = msgsz; /* * Allocate space for the message */ while (segs_needed > 0) { if (nfree_msgmaps <= 0) panic("not enough msgmaps"); if (free_msgmaps == -1) panic("nil free_msgmaps"); next = free_msgmaps; if (next <= -1) panic("next too low #1"); if (next >= msginfo.msgseg) panic("next out of range #1"); #ifdef MSG_DEBUG_OK kprintf("allocating segment %d to message\n", next); #endif free_msgmaps = msgmaps[next].next; nfree_msgmaps--; msgmaps[next].next = msghdr->msg_spot; msghdr->msg_spot = next; segs_needed--; } /* * Copy in the message type */ if ((eval = copyin(user_msgp, &msghdr->msg_type, sizeof(msghdr->msg_type))) != 0) { #ifdef MSG_DEBUG_OK kprintf("error %d copying the message type\n", eval); #endif msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; wakeup((caddr_t)msqptr); goto done; } user_msgp = (const char *)user_msgp + sizeof(msghdr->msg_type); /* * Validate the message type */ if (msghdr->msg_type < 1) { msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; wakeup((caddr_t)msqptr); #ifdef MSG_DEBUG_OK kprintf("mtype (%d) < 1\n", msghdr->msg_type); #endif eval = EINVAL; goto done; } /* * Copy in the message body */ next = msghdr->msg_spot; while (msgsz > 0) { size_t tlen; if (msgsz > msginfo.msgssz) tlen = msginfo.msgssz; else tlen = msgsz; if (next <= -1) panic("next too low #2"); if (next >= msginfo.msgseg) panic("next out of range #2"); if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen)) != 0) { #ifdef MSG_DEBUG_OK kprintf("error %d copying in message segment\n", eval); #endif msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; wakeup((caddr_t)msqptr); goto done; } msgsz -= tlen; user_msgp = (const char *)user_msgp + tlen; next = msgmaps[next].next; } if (next != -1) panic("didn't use all the msg segments"); /* * We've got the message. Unlock the msqid_ds. */ msqptr->msg_perm.mode &= ~MSG_LOCKED; /* * Make sure that the msqid_ds is still allocated. */ if (msqptr->msg_qbytes == 0) { msg_freehdr(msghdr); wakeup((caddr_t)msqptr); eval = EIDRM; goto done; } /* * Put the message into the queue */ if (msqptr->msg_first == NULL) { msqptr->msg_first = msghdr; msqptr->msg_last = msghdr; } else { msqptr->msg_last->msg_next = msghdr; msqptr->msg_last = msghdr; } msqptr->msg_last->msg_next = NULL; msqptr->msg_cbytes += msghdr->msg_ts; msqptr->msg_qnum++; msqptr->msg_lspid = td->td_proc->p_pid; msqptr->msg_stime = time_second; wakeup((caddr_t)msqptr); eval = 0; done: rel_mplock(); if (eval == 0) uap->sysmsg_result = 0; return (eval); }