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 {
static int shm_msg(resmgr_context_t *ctp, io_msg_t *pmsg, RESMGR_OCB_T *ocb) { union { io_msg_t hdr; shmmgr_get_t get; shmmgr_attach_t attach; shmmgr_detach_t detach; shmmgr_ctl_t ctl; } *msg = (void *)pmsg; struct shmid_ds_pool *sd; struct _client_info info; char buf[128]; int status; if (msg->hdr.i.mgrid != _IOMGR_SHMMGR) { return _RESMGR_DEFAULT; } // we will always need the client info if ((status = ConnectClientInfo_r(ctp->info.scoid, &info, NGROUPS_MAX)) != EOK) { return status; } switch (msg->hdr.i.subtype) { case _SHMMGR_GET: return msg_get(ctp, &msg->get, &info); case _SHMMGR_ATTACH: case _SHMMGR_DETACH: if (msg->attach.i.shmid < 0 || msg->attach.i.shmid >= shmid_array_total) { return EINVAL; } _mutex_lock(&shmid_array_mutex); sd = &shmid_array[msg->attach.i.shmid]; if (sd->shmds.shm_perm.mode & SHMSEG_FREE) { _mutex_unlock(&shmid_array_mutex); return EINVAL; } if ((status = ipcperm(&info, &sd->shmds.shm_perm, (msg->attach.i.flag & SHM_RDONLY) ? IPC_R : IPC_R | IPC_W)) != 0) { _mutex_unlock(&shmid_array_mutex); return status; } sd->shmds.shm_lpid = info.pid; sd->shmds.shm_atime = time(NULL); if (msg->hdr.i.subtype == _SHMMGR_ATTACH) sd->shmds.shm_nattch++; else sd->shmds.shm_nattch--; msg->attach.o.size = sd->shmds.shm_segsz; _mutex_unlock(&shmid_array_mutex); MsgReply(ctp->rcvid, EOK, &msg->attach.o, sizeof(msg->attach.o)); break; case _SHMMGR_CTL: if (msg->ctl.i.shmid < 0 || msg->ctl.i.shmid >= shmid_array_total) { return EINVAL; } _mutex_lock(&shmid_array_mutex); sd = &shmid_array[msg->ctl.i.shmid]; if (sd->shmds.shm_perm.mode & SHMSEG_FREE) { _mutex_unlock(&shmid_array_mutex); return EINVAL; } switch(msg->ctl.i.cmd) { case IPC_STAT: if ((status = ipcperm(&info, &sd->shmds.shm_perm, IPC_R)) != 0) { _mutex_unlock(&shmid_array_mutex); return status; } memcpy(&msg->ctl.i.buf, &sd->shmds, sizeof(sd->shmds)); break; case IPC_SET: if ((status = ipcperm(&info, &sd->shmds.shm_perm, IPC_M)) != 0) { _mutex_unlock(&shmid_array_mutex); return status; } sd->shmds.shm_perm.uid = msg->ctl.i.buf.shm_perm.uid; sd->shmds.shm_perm.gid = msg->ctl.i.buf.shm_perm.gid; sd->shmds.shm_perm.mode = (msg->ctl.i.buf.shm_perm.mode & S_IPERMS) | (sd->shmds.shm_perm.mode & ~S_IPERMS); sd->shmds.shm_ctime = time(NULL); break; case IPC_RMID: if ((status = ipcperm(&info, &sd->shmds.shm_perm, IPC_M)) != 0) { _mutex_unlock(&shmid_array_mutex); return status; } snprintf(buf, sizeof(buf), "%s/%d", PATH_SHM, msg->ctl.i.shmid); shm_unlink(buf); sd->shmds.shm_perm.mode = SHMSEG_FREE; break; } _mutex_unlock(&shmid_array_mutex); MsgReply(ctp->rcvid, EOK, &msg->ctl.o, sizeof(msg->ctl.o)); } return _RESMGR_NOREPLY; }
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 msgget(__unused struct proc *p, struct msgget_args *uap, int32_t *retval) { int msqid, eval; int key = uap->key; int msgflg = uap->msgflg; kauth_cred_t cred = kauth_cred_get(); struct msqid_kernel *msqptr = NULL; SYSV_MSG_SUBSYS_LOCK(); if (!msginit(0)) { eval = ENOMEM; goto msggetout; } #ifdef MSG_DEBUG_OK printf("msgget(0x%x, 0%o)\n", key, msgflg); #endif if (key != IPC_PRIVATE) { for (msqid = 0; msqid < msginfo.msgmni; msqid++) { msqptr = &msqids[msqid]; if (msqptr->u.msg_qbytes != 0 && msqptr->u.msg_perm._key == key) break; } if (msqid < msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("found public key\n"); #endif if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { #ifdef MSG_DEBUG_OK printf("not exclusive\n"); #endif eval = EEXIST; goto msggetout; } if ((eval = ipcperm(cred, &msqptr->u.msg_perm, msgflg & 0700 ))) { #ifdef MSG_DEBUG_OK printf("requester doesn't have 0%o access\n", msgflg & 0700); #endif goto msggetout; } #if CONFIG_MACF eval = mac_sysvmsq_check_msqget(cred, msqptr); if (eval) goto msggetout; #endif goto found; } } #ifdef MSG_DEBUG_OK printf("need to allocate the user_msqid_ds\n"); #endif if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { for (msqid = 0; msqid < msginfo.msgmni; msqid++) { /* * Look for an unallocated and unlocked user_msqid_ds. * user_msqid_ds's can be locked by msgsnd or msgrcv * while they are copying the message in/out. We * can't re-use the entry until they release it. */ msqptr = &msqids[msqid]; if (msqptr->u.msg_qbytes == 0 && (msqptr->u.msg_perm.mode & MSG_LOCKED) == 0) break; } if (msqid == msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("no more user_msqid_ds's available\n"); #endif eval = ENOSPC; goto msggetout; } #ifdef MSG_DEBUG_OK printf("msqid %d is available\n", msqid); #endif msqptr->u.msg_perm._key = key; msqptr->u.msg_perm.cuid = kauth_cred_getuid(cred); msqptr->u.msg_perm.uid = kauth_cred_getuid(cred); msqptr->u.msg_perm.cgid = kauth_cred_getgid(cred); msqptr->u.msg_perm.gid = kauth_cred_getgid(cred); msqptr->u.msg_perm.mode = (msgflg & 0777); /* Make sure that the returned msqid is unique */ msqptr->u.msg_perm._seq++; msqptr->u.msg_first = NULL; msqptr->u.msg_last = NULL; msqptr->u.msg_cbytes = 0; msqptr->u.msg_qnum = 0; msqptr->u.msg_qbytes = msginfo.msgmnb; msqptr->u.msg_lspid = 0; msqptr->u.msg_lrpid = 0; msqptr->u.msg_stime = 0; msqptr->u.msg_rtime = 0; msqptr->u.msg_ctime = sysv_msgtime(); #if CONFIG_MACF mac_sysvmsq_label_associate(cred, msqptr); #endif } else { #ifdef MSG_DEBUG_OK printf("didn't find it and wasn't asked to create it\n"); #endif eval = ENOENT; goto msggetout; } found: /* Construct the unique msqid */ *retval = IXSEQ_TO_IPCID(msqid, msqptr->u.msg_perm); AUDIT_ARG(svipc_id, *retval); eval = 0; msggetout: SYSV_MSG_SUBSYS_UNLOCK(); return(eval); }
/* Handle a shmat() request. */ int handle_shmat(pid_t pid, struct shmat_msg *shmat_msg, struct cmsgcred *cred ) { int error; int fd; struct shmid_ds *shmseg; struct pid_attached *pidatt; struct shm_handle *handle; size_t new_size = shmat_msg->size; struct client *cl; struct id_attached *idatt; /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) return (ENOSYS); again:*/ shmseg = shm_find_segment_by_shmid(shmat_msg->shmid); if (shmseg == NULL) { sysvd_print_err("shmat error: segment was not found\n"); error = EINVAL; goto done; } error = ipcperm(cred, &shmseg->shm_perm, (shmat_msg->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); if (error) goto done; handle = shmseg->shm_internal; if (shmat_msg->size > shmseg->shm_segsz) { if (handle->type != UNDOGET) { error = EINVAL; goto done; } fd = ((struct shm_handle*)shmseg->shm_internal)->fd; ftruncate(fd, round_page(new_size)); shmseg->shm_segsz = new_size; } shmseg->shm_lpid = pid; shmseg->shm_atime = time(NULL); if (handle->type != UNDOGET) shmseg->shm_nattch++; else shmseg->shm_nattch = 1; /* Only a process calls shmat and only once. If it does it for more than once that is because it called exec() and reinitialized the undo segment. */ /* Insert the pid in the segment list of attaced pids. * The list is checked in handle_shmdt so that only * attached pids can dettached from this segment. */ sysvd_print("nattch = %d pid = %d\n", shmseg->shm_nattch, pid); pidatt = malloc(sizeof(*pidatt)); pidatt->pid = pid; LIST_INSERT_HEAD(&handle->attached_list, pidatt, link); /* Add the segment at the list of attached segments of the client. * It is used when the process finishes its execution. The daemon * walks through the list to dettach the segments. */ idatt = malloc(sizeof(*idatt)); idatt->shmid = shmat_msg->shmid; cl = _hash_lookup(clientshash, pid); LIST_INSERT_HEAD(&cl->ids_attached, idatt, link); return (0); done: return (error); }
/* Handle a shmctl() request. */ int handle_shmctl(struct shmctl_msg *shmctl_msg, struct cmsgcred *cred ) { int error = 0; struct shmid_ds *shmseg, *inbuf; /* if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) return (ENOSYS); */ shmseg = shm_find_segment_by_shmid(shmctl_msg->shmid); if (shmseg == NULL) { error = EINVAL; goto done; } switch (shmctl_msg->cmd) { case IPC_STAT: sysvd_print("IPC STAT\n"); error = ipcperm(cred, &shmseg->shm_perm, IPC_R); if (error) { sysvd_print("IPC_STAT not allowed\n"); break; } shmctl_msg->buf = *shmseg; break; case IPC_SET: sysvd_print("IPC SET\n"); error = ipcperm(cred, &shmseg->shm_perm, IPC_M); if (error) { sysvd_print("IPC_SET not allowed\n"); break; } inbuf = &shmctl_msg->buf; shmseg->shm_perm.uid = inbuf->shm_perm.uid; shmseg->shm_perm.gid = inbuf->shm_perm.gid; shmseg->shm_perm.mode = (shmseg->shm_perm.mode & ~ACCESSPERMS) | (inbuf->shm_perm.mode & ACCESSPERMS); shmseg->shm_ctime = time(NULL); break; case IPC_RMID: sysvd_print("IPC RMID shmid = %d\n", shmctl_msg->shmid); error = ipcperm(cred, &shmseg->shm_perm, IPC_M); if (error) { sysvd_print("IPC_RMID not allowed\n"); break; } shmseg->shm_perm.key = IPC_PRIVATE; shmseg->shm_perm.mode |= SHMSEG_REMOVED; if (shmseg->shm_nattch <= 0) { shm_deallocate_segment(IPCID_TO_IX(shmctl_msg->shmid)); shm_last_free = IPCID_TO_IX(shmctl_msg->shmid); } else { /* In sem and msg cases, other process must be * noticed about the removal. */ struct shm_handle *internal = (struct shm_handle *)shmseg->shm_internal; mark_segment_removed(shmctl_msg->shmid, internal->type); } break; #if 0 case SHM_LOCK: case SHM_UNLOCK: #endif default: error = EINVAL; break; } done: return (error); }
/* * 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 sys_shmat(struct proc *p, void *v, register_t *retval) { struct sys_shmat_args /* { syscallarg(int) shmid; syscallarg(const void *) shmaddr; syscallarg(int) shmflg; } */ *uap = v; int error, i, flags; struct ucred *cred = p->p_ucred; struct shmid_ds *shmseg; struct shmmap_head *shmmap_h; struct shmmap_state *shmmap_s; struct shm_handle *shm_handle; vaddr_t attach_va; vm_prot_t prot; vsize_t size; shmmap_h = (struct shmmap_head *)p->p_vmspace->vm_shm; if (shmmap_h == NULL) { size = sizeof(int) + shminfo.shmseg * sizeof(struct shmmap_state); shmmap_h = malloc(size, M_SHM, M_WAITOK); shmmap_h->shmseg = shminfo.shmseg; for (i = 0, shmmap_s = shmmap_h->state; i < shmmap_h->shmseg; i++, shmmap_s++) shmmap_s->shmid = -1; p->p_vmspace->vm_shm = (caddr_t)shmmap_h; } shmseg = shm_find_segment_by_shmid(SCARG(uap, shmid)); if (shmseg == NULL) return (EINVAL); error = ipcperm(cred, &shmseg->shm_perm, (SCARG(uap, shmflg) & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); if (error) return (error); for (i = 0, shmmap_s = shmmap_h->state; i < shmmap_h->shmseg; i++) { if (shmmap_s->shmid == -1) break; shmmap_s++; } if (i >= shmmap_h->shmseg) return (EMFILE); size = round_page(shmseg->shm_segsz); prot = VM_PROT_READ; if ((SCARG(uap, shmflg) & SHM_RDONLY) == 0) prot |= VM_PROT_WRITE; flags = MAP_ANON | MAP_SHARED; if (SCARG(uap, shmaddr)) { flags |= MAP_FIXED; if (SCARG(uap, shmflg) & SHM_RND) attach_va = (vaddr_t)SCARG(uap, shmaddr) & ~(SHMLBA-1); else if (((vaddr_t)SCARG(uap, shmaddr) & (SHMLBA-1)) == 0) attach_va = (vaddr_t)SCARG(uap, shmaddr); else return (EINVAL); } else { /* This is just a hint to uvm_map() about where to put it. */ attach_va = uvm_map_hint(p, prot); } shm_handle = shmseg->shm_internal; uao_reference(shm_handle->shm_object); error = uvm_map(&p->p_vmspace->vm_map, &attach_va, size, shm_handle->shm_object, 0, 0, UVM_MAPFLAG(prot, prot, UVM_INH_SHARE, UVM_ADV_RANDOM, 0)); if (error) { uao_detach(shm_handle->shm_object); return (error); } shmmap_s->va = attach_va; shmmap_s->shmid = SCARG(uap, shmid); shmseg->shm_lpid = p->p_p->ps_mainproc->p_pid; shmseg->shm_atime = time_second; shmseg->shm_nattch++; *retval = attach_va; return (0); }
/* * MPALMOSTSAFE */ int sys_msgget(struct msgget_args *uap) { struct thread *td = curthread; int msqid, eval; int key = uap->key; int msgflg = uap->msgflg; struct ucred *cred = td->td_ucred; struct msqid_ds *msqptr = NULL; #ifdef MSG_DEBUG_OK kprintf("msgget(0x%x, 0%o)\n", key, msgflg); #endif if (!jail_sysvipc_allowed && cred->cr_prison != NULL) return (ENOSYS); eval = 0; get_mplock(); if (key != IPC_PRIVATE) { for (msqid = 0; msqid < msginfo.msgmni; msqid++) { msqptr = &msqids[msqid]; if (msqptr->msg_qbytes != 0 && msqptr->msg_perm.key == key) break; } if (msqid < msginfo.msgmni) { #ifdef MSG_DEBUG_OK kprintf("found public key\n"); #endif if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { #ifdef MSG_DEBUG_OK kprintf("not exclusive\n"); #endif eval = EEXIST; goto done; } if ((eval = ipcperm(td->td_proc, &msqptr->msg_perm, msgflg & 0700 ))) { #ifdef MSG_DEBUG_OK kprintf("requester doesn't have 0%o access\n", msgflg & 0700); #endif goto done; } goto done; } } #ifdef MSG_DEBUG_OK kprintf("need to allocate the msqid_ds\n"); #endif if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { for (msqid = 0; msqid < msginfo.msgmni; msqid++) { /* * Look for an unallocated and unlocked msqid_ds. * msqid_ds's can be locked by msgsnd or msgrcv while * they are copying the message in/out. We can't * re-use the entry until they release it. */ msqptr = &msqids[msqid]; if (msqptr->msg_qbytes == 0 && (msqptr->msg_perm.mode & MSG_LOCKED) == 0) break; } if (msqid == msginfo.msgmni) { #ifdef MSG_DEBUG_OK kprintf("no more msqid_ds's available\n"); #endif eval = ENOSPC; goto done; } #ifdef MSG_DEBUG_OK kprintf("msqid %d is available\n", msqid); #endif msqptr->msg_perm.key = key; msqptr->msg_perm.cuid = cred->cr_uid; msqptr->msg_perm.uid = cred->cr_uid; msqptr->msg_perm.cgid = cred->cr_gid; msqptr->msg_perm.gid = cred->cr_gid; msqptr->msg_perm.mode = (msgflg & 0777); /* Make sure that the returned msqid is unique */ msqptr->msg_perm.seq = (msqptr->msg_perm.seq + 1) & 0x7fff; msqptr->msg_first = NULL; msqptr->msg_last = NULL; msqptr->msg_cbytes = 0; msqptr->msg_qnum = 0; msqptr->msg_qbytes = msginfo.msgmnb; msqptr->msg_lspid = 0; msqptr->msg_lrpid = 0; msqptr->msg_stime = 0; msqptr->msg_rtime = 0; msqptr->msg_ctime = time_second; } else { #ifdef MSG_DEBUG_OK kprintf("didn't find it and wasn't asked to create it\n"); #endif eval = ENOENT; } done: rel_mplock(); /* Construct the unique msqid */ if (eval == 0) uap->sysmsg_result = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 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 sys_msgget(struct lwp *l, const struct sys_msgget_args *uap, register_t *retval) { /* { syscallarg(key_t) key; syscallarg(int) msgflg; } */ int msqid, error = 0; int key = SCARG(uap, key); int msgflg = SCARG(uap, msgflg); kauth_cred_t cred = l->l_cred; struct msqid_ds *msqptr = NULL; kmsq_t *msq; mutex_enter(&msgmutex); MSG_PRINTF(("msgget(0x%x, 0%o)\n", key, msgflg)); if (key != IPC_PRIVATE) { for (msqid = 0; msqid < msginfo.msgmni; msqid++) { msq = &msqs[msqid]; msqptr = &msq->msq_u; if (msqptr->msg_qbytes != 0 && msqptr->msg_perm._key == key) break; } if (msqid < msginfo.msgmni) { MSG_PRINTF(("found public key\n")); if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { MSG_PRINTF(("not exclusive\n")); error = EEXIST; goto unlock; } if ((error = ipcperm(cred, &msqptr->msg_perm, msgflg & 0700 ))) { MSG_PRINTF(("requester doesn't have 0%o access\n", msgflg & 0700)); goto unlock; } goto found; } } MSG_PRINTF(("need to allocate the msqid_ds\n")); if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { for (msqid = 0; msqid < msginfo.msgmni; msqid++) { /* * Look for an unallocated and unlocked msqid_ds. * msqid_ds's can be locked by msgsnd or msgrcv while * they are copying the message in/out. We can't * re-use the entry until they release it. */ msq = &msqs[msqid]; msqptr = &msq->msq_u; if (msqptr->msg_qbytes == 0 && (msqptr->msg_perm.mode & MSG_LOCKED) == 0) break; } if (msqid == msginfo.msgmni) { MSG_PRINTF(("no more msqid_ds's available\n")); error = ENOSPC; goto unlock; } MSG_PRINTF(("msqid %d is available\n", msqid)); msqptr->msg_perm._key = key; msqptr->msg_perm.cuid = kauth_cred_geteuid(cred); msqptr->msg_perm.uid = kauth_cred_geteuid(cred); msqptr->msg_perm.cgid = kauth_cred_getegid(cred); msqptr->msg_perm.gid = kauth_cred_getegid(cred); msqptr->msg_perm.mode = (msgflg & 0777); /* Make sure that the returned msqid is unique */ msqptr->msg_perm._seq++; msqptr->_msg_first = NULL; msqptr->_msg_last = NULL; msqptr->_msg_cbytes = 0; msqptr->msg_qnum = 0; msqptr->msg_qbytes = msginfo.msgmnb; msqptr->msg_lspid = 0; msqptr->msg_lrpid = 0; msqptr->msg_stime = 0; msqptr->msg_rtime = 0; msqptr->msg_ctime = time_second; } else { MSG_PRINTF(("didn't find it and wasn't asked to create it\n")); error = ENOENT; goto unlock; } found: /* Construct the unique msqid */ *retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 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); }