/* * This function handles some msgctl commands which require the rw_mutex * to be held in write mode. * NOTE: no locks must be held, the rw_mutex is taken inside this function. */ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, struct msqid_ds __user *buf, int version) { struct kern_ipc_perm *ipcp; struct msqid64_ds uninitialized_var(msqid64); struct msg_queue *msq; int err; if (cmd == IPC_SET) { if (copy_msqid_from_user(&msqid64, buf, version)) return -EFAULT; } ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd, &msqid64.msg_perm, msqid64.msg_qbytes); if (IS_ERR(ipcp)) return PTR_ERR(ipcp); msq = container_of(ipcp, struct msg_queue, q_perm); err = security_msg_queue_msgctl(msq, cmd); if (err) goto out_unlock; switch (cmd) { case IPC_RMID: freeque(ns, ipcp); goto out_up; case IPC_SET: if (msqid64.msg_qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) { err = -EPERM; goto out_unlock; } err = ipc_update_perm(&msqid64.msg_perm, ipcp); if (err) goto out_unlock; msq->q_qbytes = msqid64.msg_qbytes; msq->q_ctime = get_seconds(); /* sleeping receivers might be excluded by * stricter permissions. */ expunge_all(msq, -EAGAIN); /* sleeping senders might be able to send * due to a larger queue size. */ ss_wakeup(&msq->q_senders, 0); break; default: err = -EINVAL; } out_unlock: msg_unlock(msq); out_up: up_write(&msg_ids(ns).rw_mutex); return err; }
void msg_exit_ns(struct ipc_namespace *ns) { int i; struct msg_queue *msq; mutex_lock(&msg_ids(ns).mutex); for (i = 0; i <= msg_ids(ns).max_id; i++) { msq = msg_lock(ns, i); if (msq == NULL) continue; freeque(ns, msq, i); } mutex_unlock(&msg_ids(ns).mutex); ipc_fini_ids(ns->ids[IPC_MSG_IDS]); kfree(ns->ids[IPC_MSG_IDS]); ns->ids[IPC_MSG_IDS] = NULL; }
asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) { int err, version; struct msg_queue *msq; struct msq_setbuf setbuf; struct kern_ipc_perm *ipcp; if (msqid < 0 || cmd < 0) return -EINVAL; version = ipc_parse_version(&cmd); switch (cmd) { case IPC_INFO: case MSG_INFO: { struct msginfo msginfo; int max_id; if (!buf) return -EFAULT; /* We must not return kernel stack data. * due to padding, it's not enough * to set all member fields. */ memset(&msginfo,0,sizeof(msginfo)); msginfo.msgmni = msg_ctlmni; msginfo.msgmax = msg_ctlmax; msginfo.msgmnb = msg_ctlmnb; msginfo.msgssz = MSGSSZ; msginfo.msgseg = MSGSEG; down(&msg_ids.sem); if (cmd == MSG_INFO) { msginfo.msgpool = msg_ids.in_use; msginfo.msgmap = atomic_read(&msg_hdrs); msginfo.msgtql = atomic_read(&msg_bytes); } else { msginfo.msgmap = MSGMAP; msginfo.msgpool = MSGPOOL; msginfo.msgtql = MSGTQL; } max_id = msg_ids.max_id; up(&msg_ids.sem); if (copy_to_user (buf, &msginfo, sizeof(struct msginfo))) return -EFAULT; return (max_id < 0) ? 0: max_id; } case MSG_STAT: case IPC_STAT: { struct msqid64_ds tbuf; int success_return; if (!buf) return -EFAULT; if(cmd == MSG_STAT && msqid >= msg_ids.size) return -EINVAL; memset(&tbuf,0,sizeof(tbuf)); msq = msg_lock(msqid); if (msq == NULL) return -EINVAL; if(cmd == MSG_STAT) { success_return = msg_buildid(msqid, msq->q_perm.seq); } else { err = -EIDRM; if (msg_checkid(msq,msqid)) goto out_unlock; success_return = 0; } err = -EACCES; if (ipcperms (&msq->q_perm, S_IRUGO)) goto out_unlock; kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm); tbuf.msg_stime = msq->q_stime; tbuf.msg_rtime = msq->q_rtime; tbuf.msg_ctime = msq->q_ctime; tbuf.msg_cbytes = msq->q_cbytes; tbuf.msg_qnum = msq->q_qnum; tbuf.msg_qbytes = msq->q_qbytes; tbuf.msg_lspid = msq->q_lspid; tbuf.msg_lrpid = msq->q_lrpid; msg_unlock(msqid); if (copy_msqid_to_user(buf, &tbuf, version)) return -EFAULT; return success_return; } case IPC_SET: if (!buf) return -EFAULT; if (copy_msqid_from_user (&setbuf, buf, version)) return -EFAULT; break; case IPC_RMID: break; default: return -EINVAL; } down(&msg_ids.sem); msq = msg_lock(msqid); err=-EINVAL; if (msq == NULL) goto out_up; err = -EIDRM; if (msg_checkid(msq,msqid)) goto out_unlock_up; ipcp = &msq->q_perm; err = -EPERM; if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) /* We _could_ check for CAP_CHOWN above, but we don't */ goto out_unlock_up; switch (cmd) { case IPC_SET: { if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) goto out_unlock_up; msq->q_qbytes = setbuf.qbytes; ipcp->uid = setbuf.uid; ipcp->gid = setbuf.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (S_IRWXUGO & setbuf.mode); msq->q_ctime = CURRENT_TIME; /* sleeping receivers might be excluded by * stricter permissions. */ expunge_all(msq,-EAGAIN); /* sleeping senders might be able to send * due to a larger queue size. */ ss_wakeup(&msq->q_senders,0); msg_unlock(msqid); break; } case IPC_RMID: freeque (msqid); break; } err = 0; out_up: up(&msg_ids.sem); return err; out_unlock_up: msg_unlock(msqid); goto out_up; out_unlock: msg_unlock(msqid); return err; }
asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) { int id, err = -EINVAL; struct msqid_ds *msq; struct msqid_ds tbuf; struct ipc_perm *ipcp; lock_kernel(); if (msqid < 0 || cmd < 0) goto out; err = -EFAULT; switch (cmd) { case IPC_INFO: case MSG_INFO: if (!buf) goto out; { struct msginfo msginfo; msginfo.msgmni = MSGMNI; msginfo.msgmax = MSGMAX; msginfo.msgmnb = MSGMNB; msginfo.msgmap = MSGMAP; msginfo.msgpool = MSGPOOL; msginfo.msgtql = MSGTQL; msginfo.msgssz = MSGSSZ; msginfo.msgseg = MSGSEG; if (cmd == MSG_INFO) { msginfo.msgpool = used_queues; msginfo.msgmap = msghdrs; msginfo.msgtql = msgbytes; } err = -EFAULT; if (copy_to_user (buf, &msginfo, sizeof(struct msginfo))) goto out; err = max_msqid; goto out; } case MSG_STAT: if (!buf) goto out; err = -EINVAL; if (msqid > max_msqid) goto out; msq = msgque[msqid]; if (msq == IPC_UNUSED || msq == IPC_NOID) goto out; err = -EACCES; if (ipcperms (&msq->msg_perm, S_IRUGO)) goto out; id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid; tbuf.msg_perm = msq->msg_perm; tbuf.msg_stime = msq->msg_stime; tbuf.msg_rtime = msq->msg_rtime; tbuf.msg_ctime = msq->msg_ctime; tbuf.msg_cbytes = msq->msg_cbytes; tbuf.msg_qnum = msq->msg_qnum; tbuf.msg_qbytes = msq->msg_qbytes; tbuf.msg_lspid = msq->msg_lspid; tbuf.msg_lrpid = msq->msg_lrpid; err = -EFAULT; if (copy_to_user (buf, &tbuf, sizeof(*buf))) goto out; err = id; goto out; case IPC_SET: if (!buf) goto out; err = -EFAULT; if (!copy_from_user (&tbuf, buf, sizeof (*buf))) err = 0; break; case IPC_STAT: if (!buf) goto out; break; } id = (unsigned int) msqid % MSGMNI; msq = msgque [id]; err = -EINVAL; if (msq == IPC_UNUSED || msq == IPC_NOID) goto out; err = -EIDRM; if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) goto out; ipcp = &msq->msg_perm; switch (cmd) { case IPC_STAT: err = -EACCES; if (ipcperms (ipcp, S_IRUGO)) goto out; tbuf.msg_perm = msq->msg_perm; tbuf.msg_stime = msq->msg_stime; tbuf.msg_rtime = msq->msg_rtime; tbuf.msg_ctime = msq->msg_ctime; tbuf.msg_cbytes = msq->msg_cbytes; tbuf.msg_qnum = msq->msg_qnum; tbuf.msg_qbytes = msq->msg_qbytes; tbuf.msg_lspid = msq->msg_lspid; tbuf.msg_lrpid = msq->msg_lrpid; err = -EFAULT; if (!copy_to_user (buf, &tbuf, sizeof (*buf))) err = 0; goto out; case IPC_SET: err = -EPERM; if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) /* We _could_ check for CAP_CHOWN above, but we don't */ goto out; if (tbuf.msg_qbytes > MSGMNB && !capable(CAP_SYS_RESOURCE)) goto out; msq->msg_qbytes = tbuf.msg_qbytes; ipcp->uid = tbuf.msg_perm.uid; ipcp->gid = tbuf.msg_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (S_IRWXUGO & tbuf.msg_perm.mode); msq->msg_ctime = CURRENT_TIME; err = 0; goto out; case IPC_RMID: err = -EPERM; if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) goto out; freeque (id); err = 0; goto out; default: err = -EINVAL; goto out; } out: unlock_kernel(); return err; }