From 156d9ed1260ee566f2be09c13254b58247edfb29 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 07:57:34 -0400 Subject: msgctl(): split the actual work from copyin/copyout Signed-off-by: Al Viro --- ipc/msg.c | 202 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 96 insertions(+), 106 deletions(-) (limited to 'ipc/msg.c') diff --git a/ipc/msg.c b/ipc/msg.c index 5b25e0755656..322e7bf8b8d1 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -361,23 +361,17 @@ copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version) * NOTE: no locks must be held, the rwsem 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 msqid64_ds *msqid64) { 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; - } - down_write(&msg_ids(ns).rwsem); rcu_read_lock(); ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd, - &msqid64.msg_perm, msqid64.msg_qbytes); + &msqid64->msg_perm, msqid64->msg_qbytes); if (IS_ERR(ipcp)) { err = PTR_ERR(ipcp); goto out_unlock1; @@ -399,18 +393,18 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, { DEFINE_WAKE_Q(wake_q); - if (msqid64.msg_qbytes > ns->msg_ctlmnb && + if (msqid64->msg_qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) { err = -EPERM; goto out_unlock1; } ipc_lock_object(&msq->q_perm); - err = ipc_update_perm(&msqid64.msg_perm, ipcp); + err = ipc_update_perm(&msqid64->msg_perm, ipcp); if (err) goto out_unlock0; - msq->q_qbytes = msqid64.msg_qbytes; + msq->q_qbytes = msqid64->msg_qbytes; msq->q_ctime = get_seconds(); /* @@ -442,111 +436,89 @@ out_up: return err; } -static int msgctl_nolock(struct ipc_namespace *ns, int msqid, - int cmd, int version, void __user *buf) +static int msgctl_info(struct ipc_namespace *ns, int msqid, + int cmd, struct msginfo *msginfo) { int err; - struct msg_queue *msq; - - switch (cmd) { - case IPC_INFO: - case MSG_INFO: - { - struct msginfo msginfo; - int max_id; - - if (!buf) - return -EFAULT; + int max_id; - /* - * We must not return kernel stack data. - * due to padding, it's not enough - * to set all member fields. - */ - err = security_msg_queue_msgctl(NULL, cmd); - if (err) - return err; - - memset(&msginfo, 0, sizeof(msginfo)); - msginfo.msgmni = ns->msg_ctlmni; - msginfo.msgmax = ns->msg_ctlmax; - msginfo.msgmnb = ns->msg_ctlmnb; - msginfo.msgssz = MSGSSZ; - msginfo.msgseg = MSGSEG; - down_read(&msg_ids(ns).rwsem); - if (cmd == MSG_INFO) { - msginfo.msgpool = msg_ids(ns).in_use; - msginfo.msgmap = atomic_read(&ns->msg_hdrs); - msginfo.msgtql = atomic_read(&ns->msg_bytes); - } else { - msginfo.msgmap = MSGMAP; - msginfo.msgpool = MSGPOOL; - msginfo.msgtql = MSGTQL; - } - max_id = ipc_get_maxid(&msg_ids(ns)); - up_read(&msg_ids(ns).rwsem); - if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) - return -EFAULT; - return (max_id < 0) ? 0 : max_id; + /* + * We must not return kernel stack data. + * due to padding, it's not enough + * to set all member fields. + */ + err = security_msg_queue_msgctl(NULL, cmd); + if (err) + return err; + + memset(msginfo, 0, sizeof(*msginfo)); + msginfo->msgmni = ns->msg_ctlmni; + msginfo->msgmax = ns->msg_ctlmax; + msginfo->msgmnb = ns->msg_ctlmnb; + msginfo->msgssz = MSGSSZ; + msginfo->msgseg = MSGSEG; + down_read(&msg_ids(ns).rwsem); + if (cmd == MSG_INFO) { + msginfo->msgpool = msg_ids(ns).in_use; + msginfo->msgmap = atomic_read(&ns->msg_hdrs); + msginfo->msgtql = atomic_read(&ns->msg_bytes); + } else { + msginfo->msgmap = MSGMAP; + msginfo->msgpool = MSGPOOL; + msginfo->msgtql = MSGTQL; } + max_id = ipc_get_maxid(&msg_ids(ns)); + up_read(&msg_ids(ns).rwsem); + return (max_id < 0) ? 0 : max_id; +} - case MSG_STAT: - case IPC_STAT: - { - struct msqid64_ds tbuf; - int success_return; - - if (!buf) - return -EFAULT; - - memset(&tbuf, 0, sizeof(tbuf)); +static int msgctl_stat(struct ipc_namespace *ns, int msqid, + int cmd, struct msqid64_ds *p) +{ + int err; + struct msg_queue *msq; + int success_return; - rcu_read_lock(); - if (cmd == MSG_STAT) { - msq = msq_obtain_object(ns, msqid); - if (IS_ERR(msq)) { - err = PTR_ERR(msq); - goto out_unlock; - } - success_return = msq->q_perm.id; - } else { - msq = msq_obtain_object_check(ns, msqid); - if (IS_ERR(msq)) { - err = PTR_ERR(msq); - goto out_unlock; - } - success_return = 0; - } + memset(p, 0, sizeof(*p)); - err = -EACCES; - if (ipcperms(ns, &msq->q_perm, S_IRUGO)) + rcu_read_lock(); + if (cmd == MSG_STAT) { + msq = msq_obtain_object(ns, msqid); + if (IS_ERR(msq)) { + err = PTR_ERR(msq); goto out_unlock; - - err = security_msg_queue_msgctl(msq, cmd); - if (err) + } + success_return = msq->q_perm.id; + } else { + msq = msq_obtain_object_check(ns, msqid); + if (IS_ERR(msq)) { + err = PTR_ERR(msq); goto out_unlock; + } + success_return = 0; + } - 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; - rcu_read_unlock(); + err = -EACCES; + if (ipcperms(ns, &msq->q_perm, S_IRUGO)) + goto out_unlock; - if (copy_msqid_to_user(buf, &tbuf, version)) - return -EFAULT; - return success_return; - } + err = security_msg_queue_msgctl(msq, cmd); + if (err) + goto out_unlock; + + kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm); + p->msg_stime = msq->q_stime; + p->msg_rtime = msq->q_rtime; + p->msg_ctime = msq->q_ctime; + p->msg_cbytes = msq->q_cbytes; + p->msg_qnum = msq->q_qnum; + p->msg_qbytes = msq->q_qbytes; + p->msg_lspid = msq->q_lspid; + p->msg_lrpid = msq->q_lrpid; + rcu_read_unlock(); - default: - return -EINVAL; - } + return success_return; - return err; out_unlock: rcu_read_unlock(); return err; @@ -556,6 +528,8 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) { int version; struct ipc_namespace *ns; + struct msqid64_ds msqid64; + int err; if (msqid < 0 || cmd < 0) return -EINVAL; @@ -565,13 +539,29 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) switch (cmd) { case IPC_INFO: - case MSG_INFO: + case MSG_INFO: { + struct msginfo msginfo; + err = msgctl_info(ns, msqid, cmd, &msginfo); + if (err < 0) + return err; + if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) + err = -EFAULT; + return err; + } case MSG_STAT: /* msqid is an index rather than a msg queue id */ case IPC_STAT: - return msgctl_nolock(ns, msqid, cmd, version, buf); + err = msgctl_stat(ns, msqid, cmd, &msqid64); + if (err < 0) + return err; + if (copy_msqid_to_user(buf, &msqid64, version)) + err = -EFAULT; + return err; case IPC_SET: + if (copy_msqid_from_user(&msqid64, buf, version)) + return -EFAULT; + /* fallthru */ case IPC_RMID: - return msgctl_down(ns, msqid, cmd, buf, version); + return msgctl_down(ns, msqid, cmd, &msqid64); default: return -EINVAL; } -- cgit v1.2.3 From 4693916846269d633a3664586650dbfac2c5562f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 08:31:16 -0400 Subject: msgctl(): move compat to native Signed-off-by: Al Viro --- ipc/msg.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) (limited to 'ipc/msg.c') diff --git a/ipc/msg.c b/ipc/msg.c index 322e7bf8b8d1..3400012e1ce8 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -567,6 +567,139 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) } } +#ifdef CONFIG_COMPAT + +struct compat_msqid_ds { + struct compat_ipc_perm msg_perm; + compat_uptr_t msg_first; + compat_uptr_t msg_last; + compat_time_t msg_stime; + compat_time_t msg_rtime; + compat_time_t msg_ctime; + compat_ulong_t msg_lcbytes; + compat_ulong_t msg_lqbytes; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + compat_ipc_pid_t msg_lspid; + compat_ipc_pid_t msg_lrpid; +}; + +static int copy_compat_msqid_from_user(struct msqid64_ds *out, void __user *buf, + int version) +{ + memset(out, 0, sizeof(*out)); + if (version == IPC_64) { + struct compat_msqid64_ds *p = buf; + struct compat_ipc64_perm v; + if (copy_from_user(&v, &p->msg_perm, sizeof(v))) + return -EFAULT; + out->msg_perm.uid = v.uid; + out->msg_perm.gid = v.gid; + out->msg_perm.mode = v.mode; + if (get_user(out->msg_qbytes, &p->msg_qbytes)) + return -EFAULT; + } else { + struct compat_msqid_ds *p = buf; + struct compat_ipc_perm v; + if (copy_from_user(&v, &p->msg_perm, sizeof(v))) + return -EFAULT; + out->msg_perm.uid = v.uid; + out->msg_perm.gid = v.gid; + out->msg_perm.mode = v.mode; + if (get_user(out->msg_qbytes, &p->msg_qbytes)) + return -EFAULT; + } + return 0; +} + +static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, + int version) +{ + if (version == IPC_64) { + struct compat_msqid64_ds v; + memset(&v, 0, sizeof(v)); + v.msg_perm.key = in->msg_perm.key; + v.msg_perm.uid = in->msg_perm.uid; + v.msg_perm.gid = in->msg_perm.gid; + v.msg_perm.cuid = in->msg_perm.cuid; + v.msg_perm.cgid = in->msg_perm.cgid; + v.msg_perm.mode = in->msg_perm.mode; + v.msg_perm.seq = in->msg_perm.seq; + v.msg_stime = in->msg_stime; + v.msg_rtime = in->msg_rtime; + v.msg_ctime = in->msg_ctime; + v.msg_cbytes = in->msg_cbytes; + v.msg_qnum = in->msg_qnum; + v.msg_qbytes = in->msg_qbytes; + v.msg_lspid = in->msg_lspid; + v.msg_lrpid = in->msg_lrpid; + return copy_to_user(buf, &v, sizeof(v)); + } else { + struct compat_msqid_ds v; + memset(&v, 0, sizeof(v)); + v.msg_perm.key = in->msg_perm.key; + SET_UID(v.msg_perm.uid, in->msg_perm.uid); + SET_GID(v.msg_perm.gid, in->msg_perm.gid); + SET_UID(v.msg_perm.cuid, in->msg_perm.cuid); + SET_GID(v.msg_perm.cgid, in->msg_perm.cgid); + v.msg_perm.mode = in->msg_perm.mode; + v.msg_perm.seq = in->msg_perm.seq; + v.msg_stime = in->msg_stime; + v.msg_rtime = in->msg_rtime; + v.msg_ctime = in->msg_ctime; + v.msg_cbytes = in->msg_cbytes; + v.msg_qnum = in->msg_qnum; + v.msg_qbytes = in->msg_qbytes; + v.msg_lspid = in->msg_lspid; + v.msg_lrpid = in->msg_lrpid; + return copy_to_user(buf, &v, sizeof(v)); + } +} + +COMPAT_SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, void __user *, uptr) +{ + struct ipc_namespace *ns; + int err; + struct msqid64_ds msqid64; + int version = compat_ipc_parse_version(&cmd); + + ns = current->nsproxy->ipc_ns; + + if (msqid < 0 || cmd < 0) + return -EINVAL; + + switch (cmd & (~IPC_64)) { + case IPC_INFO: + case MSG_INFO: { + struct msginfo msginfo; + err = msgctl_info(ns, msqid, cmd, &msginfo); + if (err < 0) + return err; + if (copy_to_user(uptr, &msginfo, sizeof(struct msginfo))) + err = -EFAULT; + return err; + } + case IPC_STAT: + case MSG_STAT: + err = msgctl_stat(ns, msqid, cmd, &msqid64); + if (err < 0) + return err; + if (copy_compat_msqid_to_user(uptr, &msqid64, version)) + err = -EFAULT; + return err; + case IPC_SET: + if (copy_compat_msqid_from_user(&msqid64, uptr, version)) + return -EFAULT; + /* fallthru */ + case IPC_RMID: + return msgctl_down(ns, msqid, cmd, &msqid64); + default: + return -EINVAL; + } +} +#endif + static int testmsg(struct msg_msg *msg, long type, int mode) { switch (mode) { -- cgit v1.2.3 From 28327fae62b011216026b66299882c53b95b4500 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 10:10:32 -0400 Subject: ipc: make use of compat ipc_perm helpers Signed-off-by: Al Viro --- ipc/msg.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) (limited to 'ipc/msg.c') diff --git a/ipc/msg.c b/ipc/msg.c index 3400012e1ce8..94690fb53f66 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -591,22 +591,14 @@ static int copy_compat_msqid_from_user(struct msqid64_ds *out, void __user *buf, memset(out, 0, sizeof(*out)); if (version == IPC_64) { struct compat_msqid64_ds *p = buf; - struct compat_ipc64_perm v; - if (copy_from_user(&v, &p->msg_perm, sizeof(v))) + if (get_compat_ipc64_perm(&out->msg_perm, &p->msg_perm)) return -EFAULT; - out->msg_perm.uid = v.uid; - out->msg_perm.gid = v.gid; - out->msg_perm.mode = v.mode; if (get_user(out->msg_qbytes, &p->msg_qbytes)) return -EFAULT; } else { struct compat_msqid_ds *p = buf; - struct compat_ipc_perm v; - if (copy_from_user(&v, &p->msg_perm, sizeof(v))) + if (get_compat_ipc_perm(&out->msg_perm, &p->msg_perm)) return -EFAULT; - out->msg_perm.uid = v.uid; - out->msg_perm.gid = v.gid; - out->msg_perm.mode = v.mode; if (get_user(out->msg_qbytes, &p->msg_qbytes)) return -EFAULT; } @@ -619,13 +611,7 @@ static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, if (version == IPC_64) { struct compat_msqid64_ds v; memset(&v, 0, sizeof(v)); - v.msg_perm.key = in->msg_perm.key; - v.msg_perm.uid = in->msg_perm.uid; - v.msg_perm.gid = in->msg_perm.gid; - v.msg_perm.cuid = in->msg_perm.cuid; - v.msg_perm.cgid = in->msg_perm.cgid; - v.msg_perm.mode = in->msg_perm.mode; - v.msg_perm.seq = in->msg_perm.seq; + to_compat_ipc64_perm(&v.msg_perm, &in->msg_perm); v.msg_stime = in->msg_stime; v.msg_rtime = in->msg_rtime; v.msg_ctime = in->msg_ctime; @@ -638,13 +624,7 @@ static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, } else { struct compat_msqid_ds v; memset(&v, 0, sizeof(v)); - v.msg_perm.key = in->msg_perm.key; - SET_UID(v.msg_perm.uid, in->msg_perm.uid); - SET_GID(v.msg_perm.gid, in->msg_perm.gid); - SET_UID(v.msg_perm.cuid, in->msg_perm.cuid); - SET_GID(v.msg_perm.cgid, in->msg_perm.cgid); - v.msg_perm.mode = in->msg_perm.mode; - v.msg_perm.seq = in->msg_perm.seq; + to_compat_ipc_perm(&v.msg_perm, &in->msg_perm); v.msg_stime = in->msg_stime; v.msg_rtime = in->msg_rtime; v.msg_ctime = in->msg_ctime; -- cgit v1.2.3 From 9b1404c24a357332cb2a6df7c4337e943a4545fd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 10:34:35 -0400 Subject: msgrcv(2), msgsnd(2): move compat to native Signed-off-by: Al Viro --- ipc/msg.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'ipc/msg.c') diff --git a/ipc/msg.c b/ipc/msg.c index 94690fb53f66..855da19c765a 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -730,7 +730,7 @@ static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg, return 0; } -long do_msgsnd(int msqid, long mtype, void __user *mtext, +static long do_msgsnd(int msqid, long mtype, void __user *mtext, size_t msgsz, int msgflg) { struct msg_queue *msq; @@ -853,6 +853,25 @@ SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); } +#ifdef CONFIG_COMPAT + +struct compat_msgbuf { + compat_long_t mtype; + char mtext[1]; +}; + +COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp, + compat_ssize_t, msgsz, int, msgflg) +{ + struct compat_msgbuf __user *up = compat_ptr(msgp); + compat_long_t mtype; + + if (get_user(mtype, &up->mtype)) + return -EFAULT; + return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg); +} +#endif + static inline int convert_mode(long *msgtyp, int msgflg) { if (msgflg & MSG_COPY) @@ -949,7 +968,7 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode) return found ?: ERR_PTR(-EAGAIN); } -long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, +static long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, long (*msg_handler)(void __user *, struct msg_msg *, size_t)) { int mode; @@ -1113,6 +1132,28 @@ SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill); } +#ifdef CONFIG_COMPAT +static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) +{ + struct compat_msgbuf __user *msgp = dest; + size_t msgsz; + + if (put_user(msg->m_type, &msgp->mtype)) + return -EFAULT; + + msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; + if (store_msg(msgp->mtext, msg, msgsz)) + return -EFAULT; + return msgsz; +} + +COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp, + compat_ssize_t, msgsz, compat_long_t, msgtyp, int, msgflg) +{ + return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp, + msgflg, compat_do_msg_fill); +} +#endif void msg_init_ns(struct ipc_namespace *ns) { -- cgit v1.2.3 From 50578ea97a2a352c109bd1657e667b212faf2cbb Mon Sep 17 00:00:00 2001 From: Deepa Dinamani Date: Wed, 2 Aug 2017 19:51:12 -0700 Subject: ipc: msg: Make msg_queue timestamps y2038 safe time_t is not y2038 safe. Replace all uses of time_t by y2038 safe time64_t. Similarly, replace the calls to get_seconds() with y2038 safe ktime_get_real_seconds(). Note that this preserves fast access on 64 bit systems, but 32 bit systems need sequence counters. The syscall interfaces themselves are not changed as part of the patch. They will be part of a different series. Signed-off-by: Deepa Dinamani Reviewed-by: Arnd Bergmann Signed-off-by: Al Viro --- ipc/msg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'ipc/msg.c') diff --git a/ipc/msg.c b/ipc/msg.c index 855da19c765a..0e7ccfc0700b 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -133,7 +133,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) } msq->q_stime = msq->q_rtime = 0; - msq->q_ctime = get_seconds(); + msq->q_ctime = ktime_get_real_seconds(); msq->q_cbytes = msq->q_qnum = 0; msq->q_qbytes = ns->msg_ctlmnb; msq->q_lspid = msq->q_lrpid = 0; @@ -406,7 +406,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, msq->q_qbytes = msqid64->msg_qbytes; - msq->q_ctime = get_seconds(); + msq->q_ctime = ktime_get_real_seconds(); /* * Sleeping receivers might be excluded by * stricter permissions. @@ -1181,7 +1181,7 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it) struct msg_queue *msq = it; seq_printf(s, - "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n", + "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10llu %10llu %10llu\n", msq->q_perm.key, msq->q_perm.id, msq->q_perm.mode, -- cgit v1.2.3