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 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 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); }
/* * 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); }
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); }
/* * 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); }
/* * 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); }
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 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); }