aboutsummaryrefslogtreecommitdiff
path: root/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc')
-rw-r--r--ipc/Makefile1
-rw-r--r--ipc/compat.c741
-rw-r--r--ipc/mqueue.c28
-rw-r--r--ipc/msg.c380
-rw-r--r--ipc/msgutil.c2
-rw-r--r--ipc/namespace.c25
-rw-r--r--ipc/sem.c376
-rw-r--r--ipc/shm.c550
-rw-r--r--ipc/syscall.c91
-rw-r--r--ipc/util.c107
-rw-r--r--ipc/util.h52
11 files changed, 1165 insertions, 1188 deletions
diff --git a/ipc/Makefile b/ipc/Makefile
index 9c200e544434..c2558c430f51 100644
--- a/ipc/Makefile
+++ b/ipc/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux ipc.
#
diff --git a/ipc/compat.c b/ipc/compat.c
index 9b3c85f8a538..5ab8225923af 100644
--- a/ipc/compat.c
+++ b/ipc/compat.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* 32 bit compatibility code for System V IPC
*
@@ -34,724 +35,48 @@
#include "util.h"
-struct compat_msgbuf {
- compat_long_t mtype;
- char mtext[1];
-};
-
-struct compat_ipc_perm {
- key_t key;
- __compat_uid_t uid;
- __compat_gid_t gid;
- __compat_uid_t cuid;
- __compat_gid_t cgid;
- compat_mode_t mode;
- unsigned short seq;
-};
-
-struct compat_semid_ds {
- struct compat_ipc_perm sem_perm;
- compat_time_t sem_otime;
- compat_time_t sem_ctime;
- compat_uptr_t sem_base;
- compat_uptr_t sem_pending;
- compat_uptr_t sem_pending_last;
- compat_uptr_t undo;
- unsigned short sem_nsems;
-};
-
-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;
-};
-
-struct compat_shmid_ds {
- struct compat_ipc_perm shm_perm;
- int shm_segsz;
- compat_time_t shm_atime;
- compat_time_t shm_dtime;
- compat_time_t shm_ctime;
- compat_ipc_pid_t shm_cpid;
- compat_ipc_pid_t shm_lpid;
- unsigned short shm_nattch;
- unsigned short shm_unused;
- compat_uptr_t shm_unused2;
- compat_uptr_t shm_unused3;
-};
-
-struct compat_ipc_kludge {
- compat_uptr_t msgp;
- compat_long_t msgtyp;
-};
-
-struct compat_shminfo64 {
- compat_ulong_t shmmax;
- compat_ulong_t shmmin;
- compat_ulong_t shmmni;
- compat_ulong_t shmseg;
- compat_ulong_t shmall;
- compat_ulong_t __unused1;
- compat_ulong_t __unused2;
- compat_ulong_t __unused3;
- compat_ulong_t __unused4;
-};
-
-struct compat_shm_info {
- compat_int_t used_ids;
- compat_ulong_t shm_tot, shm_rss, shm_swp;
- compat_ulong_t swap_attempts, swap_successes;
-};
-
-static inline int compat_ipc_parse_version(int *cmd)
-{
-#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION
- int version = *cmd & IPC_64;
-
- /* this is tricky: architectures that have support for the old
- * ipc structures in 64 bit binaries need to have IPC_64 set
- * in cmd, the others need to have it cleared */
-#ifndef ipc_parse_version
- *cmd |= IPC_64;
-#else
- *cmd &= ~IPC_64;
-#endif
- return version;
-#else
- /* With the asm-generic APIs, we always use the 64-bit versions. */
- return IPC_64;
-#endif
-}
-
-static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64,
- struct compat_ipc64_perm __user *up64)
-{
- int err;
-
- err = __get_user(p64->uid, &up64->uid);
- err |= __get_user(p64->gid, &up64->gid);
- err |= __get_user(p64->mode, &up64->mode);
- return err;
-}
-
-static inline int __get_compat_ipc_perm(struct ipc64_perm *p,
- struct compat_ipc_perm __user *up)
-{
- int err;
-
- err = __get_user(p->uid, &up->uid);
- err |= __get_user(p->gid, &up->gid);
- err |= __get_user(p->mode, &up->mode);
- return err;
-}
-
-static inline int __put_compat_ipc64_perm(struct ipc64_perm *p64,
- struct compat_ipc64_perm __user *up64)
+int get_compat_ipc64_perm(struct ipc64_perm *to,
+ struct compat_ipc64_perm __user *from)
{
- int err;
-
- err = __put_user(p64->key, &up64->key);
- err |= __put_user(p64->uid, &up64->uid);
- err |= __put_user(p64->gid, &up64->gid);
- err |= __put_user(p64->cuid, &up64->cuid);
- err |= __put_user(p64->cgid, &up64->cgid);
- err |= __put_user(p64->mode, &up64->mode);
- err |= __put_user(p64->seq, &up64->seq);
- return err;
-}
-
-static inline int __put_compat_ipc_perm(struct ipc64_perm *p,
- struct compat_ipc_perm __user *uip)
-{
- int err;
- __compat_uid_t u;
- __compat_gid_t g;
-
- err = __put_user(p->key, &uip->key);
- SET_UID(u, p->uid);
- err |= __put_user(u, &uip->uid);
- SET_GID(g, p->gid);
- err |= __put_user(g, &uip->gid);
- SET_UID(u, p->cuid);
- err |= __put_user(u, &uip->cuid);
- SET_GID(g, p->cgid);
- err |= __put_user(g, &uip->cgid);
- err |= __put_user(p->mode, &uip->mode);
- err |= __put_user(p->seq, &uip->seq);
- return err;
-}
-
-static inline int get_compat_semid64_ds(struct semid64_ds *sem64,
- struct compat_semid64_ds __user *up64)
-{
- if (!access_ok(VERIFY_READ, up64, sizeof(*up64)))
- return -EFAULT;
- return __get_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm);
-}
-
-static inline int get_compat_semid_ds(struct semid64_ds *s,
- struct compat_semid_ds __user *up)
-{
- if (!access_ok(VERIFY_READ, up, sizeof(*up)))
+ struct compat_ipc64_perm v;
+ if (copy_from_user(&v, from, sizeof(v)))
return -EFAULT;
- return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm);
+ to->uid = v.uid;
+ to->gid = v.gid;
+ to->mode = v.mode;
+ return 0;
}
-static inline int put_compat_semid64_ds(struct semid64_ds *sem64,
- struct compat_semid64_ds __user *up64)
+int get_compat_ipc_perm(struct ipc64_perm *to,
+ struct compat_ipc_perm __user *from)
{
- int err;
-
- if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
+ struct compat_ipc_perm v;
+ if (copy_from_user(&v, from, sizeof(v)))
return -EFAULT;
- err = __put_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm);
- err |= __put_user(sem64->sem_otime, &up64->sem_otime);
- err |= __put_user(sem64->sem_ctime, &up64->sem_ctime);
- err |= __put_user(sem64->sem_nsems, &up64->sem_nsems);
- return err;
+ to->uid = v.uid;
+ to->gid = v.gid;
+ to->mode = v.mode;
+ return 0;
}
-static inline int put_compat_semid_ds(struct semid64_ds *s,
- struct compat_semid_ds __user *up)
+void to_compat_ipc64_perm(struct compat_ipc64_perm *to, struct ipc64_perm *from)
{
- int err;
-
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
- return -EFAULT;
- err = __put_compat_ipc_perm(&s->sem_perm, &up->sem_perm);
- err |= __put_user(s->sem_otime, &up->sem_otime);
- err |= __put_user(s->sem_ctime, &up->sem_ctime);
- err |= __put_user(s->sem_nsems, &up->sem_nsems);
- return err;
+ to->key = from->key;
+ to->uid = from->uid;
+ to->gid = from->gid;
+ to->cuid = from->cuid;
+ to->cgid = from->cgid;
+ to->mode = from->mode;
+ to->seq = from->seq;
}
-static long do_compat_semctl(int first, int second, int third, u32 pad)
+void to_compat_ipc_perm(struct compat_ipc_perm *to, struct ipc64_perm *from)
{
- unsigned long fourth;
- int err, err2;
- struct semid64_ds sem64;
- struct semid64_ds __user *up64;
- int version = compat_ipc_parse_version(&third);
-
- memset(&sem64, 0, sizeof(sem64));
-
- if ((third & (~IPC_64)) == SETVAL)
-#ifdef __BIG_ENDIAN
- fourth = (unsigned long)pad << 32;
-#else
- fourth = pad;
-#endif
- else
- fourth = (unsigned long)compat_ptr(pad);
- switch (third & (~IPC_64)) {
- case IPC_INFO:
- case IPC_RMID:
- case SEM_INFO:
- case GETVAL:
- case GETPID:
- case GETNCNT:
- case GETZCNT:
- case GETALL:
- case SETVAL:
- case SETALL:
- err = sys_semctl(first, second, third, fourth);
- break;
-
- case IPC_STAT:
- case SEM_STAT:
- up64 = compat_alloc_user_space(sizeof(sem64));
- fourth = (unsigned long)up64;
- err = sys_semctl(first, second, third, fourth);
- if (err < 0)
- break;
- if (copy_from_user(&sem64, up64, sizeof(sem64)))
- err2 = -EFAULT;
- else if (version == IPC_64)
- err2 = put_compat_semid64_ds(&sem64, compat_ptr(pad));
- else
- err2 = put_compat_semid_ds(&sem64, compat_ptr(pad));
- if (err2)
- err = -EFAULT;
- break;
-
- case IPC_SET:
- if (version == IPC_64)
- err = get_compat_semid64_ds(&sem64, compat_ptr(pad));
- else
- err = get_compat_semid_ds(&sem64, compat_ptr(pad));
-
- up64 = compat_alloc_user_space(sizeof(sem64));
- if (copy_to_user(up64, &sem64, sizeof(sem64)))
- err = -EFAULT;
- if (err)
- break;
-
- fourth = (unsigned long)up64;
- err = sys_semctl(first, second, third, fourth);
- break;
-
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-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;
-}
-
-#ifndef COMPAT_SHMLBA
-#define COMPAT_SHMLBA SHMLBA
-#endif
-
-#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
-COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second,
- u32, third, compat_uptr_t, ptr, u32, fifth)
-{
- int version;
- u32 pad;
-
- version = call >> 16; /* hack for backward compatibility */
- call &= 0xffff;
-
- switch (call) {
- case SEMOP:
- /* struct sembuf is the same on 32 and 64bit :)) */
- return sys_semtimedop(first, compat_ptr(ptr), second, NULL);
- case SEMTIMEDOP:
- return compat_sys_semtimedop(first, compat_ptr(ptr), second,
- compat_ptr(fifth));
- case SEMGET:
- return sys_semget(first, second, third);
- case SEMCTL:
- if (!ptr)
- return -EINVAL;
- if (get_user(pad, (u32 __user *) compat_ptr(ptr)))
- return -EFAULT;
- return do_compat_semctl(first, second, third, pad);
-
- case MSGSND: {
- struct compat_msgbuf __user *up = compat_ptr(ptr);
- compat_long_t type;
-
- if (first < 0 || second < 0)
- return -EINVAL;
-
- if (get_user(type, &up->mtype))
- return -EFAULT;
-
- return do_msgsnd(first, type, up->mtext, second, third);
- }
- case MSGRCV: {
- void __user *uptr = compat_ptr(ptr);
-
- if (first < 0 || second < 0)
- return -EINVAL;
-
- if (!version) {
- struct compat_ipc_kludge ipck;
- if (!uptr)
- return -EINVAL;
- if (copy_from_user(&ipck, uptr, sizeof(ipck)))
- return -EFAULT;
- uptr = compat_ptr(ipck.msgp);
- fifth = ipck.msgtyp;
- }
- return do_msgrcv(first, uptr, second, (s32)fifth, third,
- compat_do_msg_fill);
- }
- case MSGGET:
- return sys_msgget(first, second);
- case MSGCTL:
- return compat_sys_msgctl(first, second, compat_ptr(ptr));
-
- case SHMAT: {
- int err;
- unsigned long raddr;
-
- if (version == 1)
- return -EINVAL;
- err = do_shmat(first, compat_ptr(ptr), second, &raddr,
- COMPAT_SHMLBA);
- if (err < 0)
- return err;
- return put_user(raddr, (compat_ulong_t *)compat_ptr(third));
- }
- case SHMDT:
- return sys_shmdt(compat_ptr(ptr));
- case SHMGET:
- return sys_shmget(first, (unsigned)second, third);
- case SHMCTL:
- return compat_sys_shmctl(first, second, compat_ptr(ptr));
- }
-
- return -ENOSYS;
-}
-#endif
-
-COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg)
-{
- return do_compat_semctl(semid, semnum, cmd, arg);
-}
-
-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);
-}
-
-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);
-}
-
-static inline int get_compat_msqid64(struct msqid64_ds *m64,
- struct compat_msqid64_ds __user *up64)
-{
- int err;
-
- if (!access_ok(VERIFY_READ, up64, sizeof(*up64)))
- return -EFAULT;
- err = __get_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm);
- err |= __get_user(m64->msg_qbytes, &up64->msg_qbytes);
- return err;
-}
-
-static inline int get_compat_msqid(struct msqid64_ds *m,
- struct compat_msqid_ds __user *up)
-{
- int err;
-
- if (!access_ok(VERIFY_READ, up, sizeof(*up)))
- return -EFAULT;
- err = __get_compat_ipc_perm(&m->msg_perm, &up->msg_perm);
- err |= __get_user(m->msg_qbytes, &up->msg_qbytes);
- return err;
-}
-
-static inline int put_compat_msqid64_ds(struct msqid64_ds *m64,
- struct compat_msqid64_ds __user *up64)
-{
- int err;
-
- if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
- return -EFAULT;
- err = __put_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm);
- err |= __put_user(m64->msg_stime, &up64->msg_stime);
- err |= __put_user(m64->msg_rtime, &up64->msg_rtime);
- err |= __put_user(m64->msg_ctime, &up64->msg_ctime);
- err |= __put_user(m64->msg_cbytes, &up64->msg_cbytes);
- err |= __put_user(m64->msg_qnum, &up64->msg_qnum);
- err |= __put_user(m64->msg_qbytes, &up64->msg_qbytes);
- err |= __put_user(m64->msg_lspid, &up64->msg_lspid);
- err |= __put_user(m64->msg_lrpid, &up64->msg_lrpid);
- return err;
-}
-
-static inline int put_compat_msqid_ds(struct msqid64_ds *m,
- struct compat_msqid_ds __user *up)
-{
- int err;
-
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
- return -EFAULT;
- err = __put_compat_ipc_perm(&m->msg_perm, &up->msg_perm);
- err |= __put_user(m->msg_stime, &up->msg_stime);
- err |= __put_user(m->msg_rtime, &up->msg_rtime);
- err |= __put_user(m->msg_ctime, &up->msg_ctime);
- err |= __put_user(m->msg_cbytes, &up->msg_cbytes);
- err |= __put_user(m->msg_qnum, &up->msg_qnum);
- err |= __put_user(m->msg_qbytes, &up->msg_qbytes);
- err |= __put_user(m->msg_lspid, &up->msg_lspid);
- err |= __put_user(m->msg_lrpid, &up->msg_lrpid);
- return err;
-}
-
-COMPAT_SYSCALL_DEFINE3(msgctl, int, first, int, second, void __user *, uptr)
-{
- int err, err2;
- struct msqid64_ds m64;
- int version = compat_ipc_parse_version(&second);
- void __user *p;
-
- memset(&m64, 0, sizeof(m64));
-
- switch (second & (~IPC_64)) {
- case IPC_INFO:
- case IPC_RMID:
- case MSG_INFO:
- err = sys_msgctl(first, second, uptr);
- break;
-
- case IPC_SET:
- if (version == IPC_64)
- err = get_compat_msqid64(&m64, uptr);
- else
- err = get_compat_msqid(&m64, uptr);
-
- if (err)
- break;
- p = compat_alloc_user_space(sizeof(m64));
- if (copy_to_user(p, &m64, sizeof(m64)))
- err = -EFAULT;
- else
- err = sys_msgctl(first, second, p);
- break;
-
- case IPC_STAT:
- case MSG_STAT:
- p = compat_alloc_user_space(sizeof(m64));
- err = sys_msgctl(first, second, p);
- if (err < 0)
- break;
- if (copy_from_user(&m64, p, sizeof(m64)))
- err2 = -EFAULT;
- else if (version == IPC_64)
- err2 = put_compat_msqid64_ds(&m64, uptr);
- else
- err2 = put_compat_msqid_ds(&m64, uptr);
- if (err2)
- err = -EFAULT;
- break;
-
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg)
-{
- unsigned long ret;
- long err;
-
- err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA);
- if (err)
- return err;
- force_successful_syscall_return();
- return (long)ret;
-}
-
-static inline int get_compat_shmid64_ds(struct shmid64_ds *sem64,
- struct compat_shmid64_ds __user *up64)
-{
- if (!access_ok(VERIFY_READ, up64, sizeof(*up64)))
- return -EFAULT;
- return __get_compat_ipc64_perm(&sem64->shm_perm, &up64->shm_perm);
-}
-
-static inline int get_compat_shmid_ds(struct shmid64_ds *s,
- struct compat_shmid_ds __user *up)
-{
- if (!access_ok(VERIFY_READ, up, sizeof(*up)))
- return -EFAULT;
- return __get_compat_ipc_perm(&s->shm_perm, &up->shm_perm);
-}
-
-static inline int put_compat_shmid64_ds(struct shmid64_ds *sem64,
- struct compat_shmid64_ds __user *up64)
-{
- int err;
-
- if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
- return -EFAULT;
- err = __put_compat_ipc64_perm(&sem64->shm_perm, &up64->shm_perm);
- err |= __put_user(sem64->shm_atime, &up64->shm_atime);
- err |= __put_user(sem64->shm_dtime, &up64->shm_dtime);
- err |= __put_user(sem64->shm_ctime, &up64->shm_ctime);
- err |= __put_user(sem64->shm_segsz, &up64->shm_segsz);
- err |= __put_user(sem64->shm_nattch, &up64->shm_nattch);
- err |= __put_user(sem64->shm_cpid, &up64->shm_cpid);
- err |= __put_user(sem64->shm_lpid, &up64->shm_lpid);
- return err;
-}
-
-static inline int put_compat_shmid_ds(struct shmid64_ds *s,
- struct compat_shmid_ds __user *up)
-{
- int err;
-
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
- return -EFAULT;
- err = __put_compat_ipc_perm(&s->shm_perm, &up->shm_perm);
- err |= __put_user(s->shm_atime, &up->shm_atime);
- err |= __put_user(s->shm_dtime, &up->shm_dtime);
- err |= __put_user(s->shm_ctime, &up->shm_ctime);
- err |= __put_user(s->shm_segsz, &up->shm_segsz);
- err |= __put_user(s->shm_nattch, &up->shm_nattch);
- err |= __put_user(s->shm_cpid, &up->shm_cpid);
- err |= __put_user(s->shm_lpid, &up->shm_lpid);
- return err;
-}
-
-static inline int put_compat_shminfo64(struct shminfo64 *smi,
- struct compat_shminfo64 __user *up64)
-{
- int err;
-
- if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
- return -EFAULT;
- if (smi->shmmax > INT_MAX)
- smi->shmmax = INT_MAX;
- err = __put_user(smi->shmmax, &up64->shmmax);
- err |= __put_user(smi->shmmin, &up64->shmmin);
- err |= __put_user(smi->shmmni, &up64->shmmni);
- err |= __put_user(smi->shmseg, &up64->shmseg);
- err |= __put_user(smi->shmall, &up64->shmall);
- return err;
-}
-
-static inline int put_compat_shminfo(struct shminfo64 *smi,
- struct shminfo __user *up)
-{
- int err;
-
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
- return -EFAULT;
- if (smi->shmmax > INT_MAX)
- smi->shmmax = INT_MAX;
- err = __put_user(smi->shmmax, &up->shmmax);
- err |= __put_user(smi->shmmin, &up->shmmin);
- err |= __put_user(smi->shmmni, &up->shmmni);
- err |= __put_user(smi->shmseg, &up->shmseg);
- err |= __put_user(smi->shmall, &up->shmall);
- return err;
-}
-
-static inline int put_compat_shm_info(struct shm_info __user *ip,
- struct compat_shm_info __user *uip)
-{
- int err;
- struct shm_info si;
-
- if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip)) ||
- copy_from_user(&si, ip, sizeof(si)))
- return -EFAULT;
- err = __put_user(si.used_ids, &uip->used_ids);
- err |= __put_user(si.shm_tot, &uip->shm_tot);
- err |= __put_user(si.shm_rss, &uip->shm_rss);
- err |= __put_user(si.shm_swp, &uip->shm_swp);
- err |= __put_user(si.swap_attempts, &uip->swap_attempts);
- err |= __put_user(si.swap_successes, &uip->swap_successes);
- return err;
-}
-
-COMPAT_SYSCALL_DEFINE3(shmctl, int, first, int, second, void __user *, uptr)
-{
- void __user *p;
- struct shmid64_ds sem64;
- struct shminfo64 smi;
- int err, err2;
- int version = compat_ipc_parse_version(&second);
-
- memset(&sem64, 0, sizeof(sem64));
-
- switch (second & (~IPC_64)) {
- case IPC_RMID:
- case SHM_LOCK:
- case SHM_UNLOCK:
- err = sys_shmctl(first, second, uptr);
- break;
-
- case IPC_INFO:
- p = compat_alloc_user_space(sizeof(smi));
- err = sys_shmctl(first, second, p);
- if (err < 0)
- break;
- if (copy_from_user(&smi, p, sizeof(smi)))
- err2 = -EFAULT;
- else if (version == IPC_64)
- err2 = put_compat_shminfo64(&smi, uptr);
- else
- err2 = put_compat_shminfo(&smi, uptr);
- if (err2)
- err = -EFAULT;
- break;
-
-
- case IPC_SET:
- if (version == IPC_64)
- err = get_compat_shmid64_ds(&sem64, uptr);
- else
- err = get_compat_shmid_ds(&sem64, uptr);
-
- if (err)
- break;
- p = compat_alloc_user_space(sizeof(sem64));
- if (copy_to_user(p, &sem64, sizeof(sem64)))
- err = -EFAULT;
- else
- err = sys_shmctl(first, second, p);
- break;
-
- case IPC_STAT:
- case SHM_STAT:
- p = compat_alloc_user_space(sizeof(sem64));
- err = sys_shmctl(first, second, p);
- if (err < 0)
- break;
- if (copy_from_user(&sem64, p, sizeof(sem64)))
- err2 = -EFAULT;
- else if (version == IPC_64)
- err2 = put_compat_shmid64_ds(&sem64, uptr);
- else
- err2 = put_compat_shmid_ds(&sem64, uptr);
- if (err2)
- err = -EFAULT;
- break;
-
- case SHM_INFO:
- p = compat_alloc_user_space(sizeof(struct shm_info));
- err = sys_shmctl(first, second, p);
- if (err < 0)
- break;
- err2 = put_compat_shm_info(p, uptr);
- if (err2)
- err = -EFAULT;
- break;
-
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems,
- unsigned, nsops,
- const struct compat_timespec __user *, timeout)
-{
- struct timespec __user *ts64;
- if (compat_convert_timespec(&ts64, timeout))
- return -EFAULT;
- return sys_semtimedop(semid, tsems, nsops, ts64);
+ to->key = from->key;
+ SET_UID(to->uid, from->uid);
+ SET_GID(to->gid, from->gid);
+ SET_UID(to->cuid, from->cuid);
+ SET_GID(to->cgid, from->cgid);
+ to->mode = from->mode;
+ to->seq = from->seq;
}
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index eb1391b52c6f..d24025626310 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -668,11 +668,11 @@ static void __do_notify(struct mqueue_inode_info *info)
}
static int prepare_timeout(const struct timespec __user *u_abs_timeout,
- struct timespec *ts)
+ struct timespec64 *ts)
{
- if (copy_from_user(ts, u_abs_timeout, sizeof(struct timespec)))
+ if (get_timespec64(ts, u_abs_timeout))
return -EFAULT;
- if (!timespec_valid(ts))
+ if (!timespec64_valid(ts))
return -EINVAL;
return 0;
}
@@ -962,7 +962,7 @@ static inline void pipelined_receive(struct wake_q_head *wake_q,
static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
size_t msg_len, unsigned int msg_prio,
- struct timespec *ts)
+ struct timespec64 *ts)
{
struct fd f;
struct inode *inode;
@@ -979,7 +979,7 @@ static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
return -EINVAL;
if (ts) {
- expires = timespec_to_ktime(*ts);
+ expires = timespec64_to_ktime(*ts);
timeout = &expires;
}
@@ -1080,7 +1080,7 @@ out:
static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
size_t msg_len, unsigned int __user *u_msg_prio,
- struct timespec *ts)
+ struct timespec64 *ts)
{
ssize_t ret;
struct msg_msg *msg_ptr;
@@ -1092,7 +1092,7 @@ static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
struct posix_msg_tree_node *new_leaf = NULL;
if (ts) {
- expires = timespec_to_ktime(*ts);
+ expires = timespec64_to_ktime(*ts);
timeout = &expires;
}
@@ -1184,7 +1184,7 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
size_t, msg_len, unsigned int, msg_prio,
const struct timespec __user *, u_abs_timeout)
{
- struct timespec ts, *p = NULL;
+ struct timespec64 ts, *p = NULL;
if (u_abs_timeout) {
int res = prepare_timeout(u_abs_timeout, &ts);
if (res)
@@ -1198,7 +1198,7 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
size_t, msg_len, unsigned int __user *, u_msg_prio,
const struct timespec __user *, u_abs_timeout)
{
- struct timespec ts, *p = NULL;
+ struct timespec64 ts, *p = NULL;
if (u_abs_timeout) {
int res = prepare_timeout(u_abs_timeout, &ts);
if (res)
@@ -1475,11 +1475,11 @@ COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name,
}
static int compat_prepare_timeout(const struct compat_timespec __user *p,
- struct timespec *ts)
+ struct timespec64 *ts)
{
- if (compat_get_timespec(ts, p))
+ if (compat_get_timespec64(ts, p))
return -EFAULT;
- if (!timespec_valid(ts))
+ if (!timespec64_valid(ts))
return -EINVAL;
return 0;
}
@@ -1489,7 +1489,7 @@ COMPAT_SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes,
compat_size_t, msg_len, unsigned int, msg_prio,
const struct compat_timespec __user *, u_abs_timeout)
{
- struct timespec ts, *p = NULL;
+ struct timespec64 ts, *p = NULL;
if (u_abs_timeout) {
int res = compat_prepare_timeout(u_abs_timeout, &ts);
if (res)
@@ -1504,7 +1504,7 @@ COMPAT_SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes,
compat_size_t, msg_len, unsigned int __user *, u_msg_prio,
const struct compat_timespec __user *, u_abs_timeout)
{
- struct timespec ts, *p = NULL;
+ struct timespec64 ts, *p = NULL;
if (u_abs_timeout) {
int res = compat_prepare_timeout(u_abs_timeout, &ts);
if (res)
diff --git a/ipc/msg.c b/ipc/msg.c
index 5b25e0755656..bce7ac1c8099 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/ipc/msg.c
* Copyright (C) 1992 Krishna Balasubramanian
@@ -133,7 +134,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;
@@ -361,23 +362,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,20 +394,20 @@ 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();
+ msq->q_ctime = ktime_get_real_seconds();
/*
* Sleeping receivers might be excluded by
* stricter permissions.
@@ -442,111 +437,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;
-
- /*
- * 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;
+ int max_id;
- 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;
+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;
- memset(&tbuf, 0, sizeof(tbuf));
+ memset(p, 0, sizeof(*p));
- 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;
- }
-
- 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 +529,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,18 +540,147 @@ 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;
}
}
+#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;
+ if (get_compat_ipc64_perm(&out->msg_perm, &p->msg_perm))
+ return -EFAULT;
+ if (get_user(out->msg_qbytes, &p->msg_qbytes))
+ return -EFAULT;
+ } else {
+ struct compat_msqid_ds *p = buf;
+ if (get_compat_ipc_perm(&out->msg_perm, &p->msg_perm))
+ return -EFAULT;
+ 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));
+ 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;
+ 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));
+ 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;
+ 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) {
@@ -627,7 +731,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;
@@ -750,6 +854,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)
@@ -846,7 +969,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;
@@ -1010,8 +1133,30 @@ 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)
+int msg_init_ns(struct ipc_namespace *ns)
{
ns->msg_ctlmax = MSGMAX;
ns->msg_ctlmnb = MSGMNB;
@@ -1019,7 +1164,7 @@ void msg_init_ns(struct ipc_namespace *ns)
atomic_set(&ns->msg_bytes, 0);
atomic_set(&ns->msg_hdrs, 0);
- ipc_init_ids(&ns->ids[IPC_MSG_IDS]);
+ return ipc_init_ids(&ns->ids[IPC_MSG_IDS]);
}
#ifdef CONFIG_IPC_NS
@@ -1027,6 +1172,7 @@ void msg_exit_ns(struct ipc_namespace *ns)
{
free_ipcs(ns, &msg_ids(ns), freeque);
idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr);
+ rhashtable_destroy(&ns->ids[IPC_MSG_IDS].key_ht);
}
#endif
@@ -1034,10 +1180,11 @@ void msg_exit_ns(struct ipc_namespace *ns)
static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
{
struct user_namespace *user_ns = seq_user_ns(s);
- struct msg_queue *msq = it;
+ struct kern_ipc_perm *ipcp = it;
+ struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
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,
@@ -1057,11 +1204,12 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
}
#endif
-void __init msg_init(void)
+int __init msg_init(void)
{
- msg_init_ns(&init_ipc_ns);
+ const int err = msg_init_ns(&init_ipc_ns);
ipc_init_proc_interface("sysvipc/msg",
" key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n",
IPC_MSG_IDS, sysvipc_msg_proc_show);
+ return err;
}
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index bf74eaa5c39f..84598025a6ad 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -29,7 +29,7 @@ DEFINE_SPINLOCK(mq_lock);
* and not CONFIG_IPC_NS.
*/
struct ipc_namespace init_ipc_ns = {
- .count = ATOMIC_INIT(1),
+ .count = REFCOUNT_INIT(1),
.user_ns = &init_user_ns,
.ns.inum = PROC_IPC_INIT_INO,
#ifdef CONFIG_IPC_NS
diff --git a/ipc/namespace.c b/ipc/namespace.c
index b4d80f9f7246..f59a89966f92 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/ipc/namespace.c
* Copyright (C) 2006 Pavel Emelyanov <xemul@openvz.org> OpenVZ, SWsoft Inc.
@@ -50,20 +51,32 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
goto fail_free;
ns->ns.ops = &ipcns_operations;
- atomic_set(&ns->count, 1);
+ refcount_set(&ns->count, 1);
ns->user_ns = get_user_ns(user_ns);
ns->ucounts = ucounts;
- err = mq_init_ns(ns);
+ err = sem_init_ns(ns);
if (err)
goto fail_put;
+ err = msg_init_ns(ns);
+ if (err)
+ goto fail_destroy_sem;
+ err = shm_init_ns(ns);
+ if (err)
+ goto fail_destroy_msg;
- sem_init_ns(ns);
- msg_init_ns(ns);
- shm_init_ns(ns);
+ err = mq_init_ns(ns);
+ if (err)
+ goto fail_destroy_shm;
return ns;
+fail_destroy_shm:
+ shm_exit_ns(ns);
+fail_destroy_msg:
+ msg_exit_ns(ns);
+fail_destroy_sem:
+ sem_exit_ns(ns);
fail_put:
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
@@ -144,7 +157,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)
*/
void put_ipc_ns(struct ipc_namespace *ns)
{
- if (atomic_dec_and_lock(&ns->count, &mq_lock)) {
+ if (refcount_dec_and_lock(&ns->count, &mq_lock)) {
mq_clear_sbinfo(ns);
spin_unlock(&mq_lock);
mq_put_mnt(ns);
diff --git a/ipc/sem.c b/ipc/sem.c
index 9e70cd7a17da..b2698ebdcb31 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/ipc/sem.c
* Copyright (C) 1992 Krishna Balasubramanian
@@ -122,7 +123,7 @@ struct sem_undo {
* that may be shared among all a CLONE_SYSVSEM task group.
*/
struct sem_undo_list {
- atomic_t refcnt;
+ refcount_t refcnt;
spinlock_t lock;
struct list_head list_proc;
};
@@ -130,8 +131,6 @@ struct sem_undo_list {
#define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS])
-#define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid)
-
static int newary(struct ipc_namespace *, struct ipc_params *);
static void freeary(struct ipc_namespace *, struct kern_ipc_perm *);
#ifdef CONFIG_PROC_FS
@@ -185,14 +184,14 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
#define sc_semopm sem_ctls[2]
#define sc_semmni sem_ctls[3]
-void sem_init_ns(struct ipc_namespace *ns)
+int sem_init_ns(struct ipc_namespace *ns)
{
ns->sc_semmsl = SEMMSL;
ns->sc_semmns = SEMMNS;
ns->sc_semopm = SEMOPM;
ns->sc_semmni = SEMMNI;
ns->used_sems = 0;
- ipc_init_ids(&ns->ids[IPC_SEM_IDS]);
+ return ipc_init_ids(&ns->ids[IPC_SEM_IDS]);
}
#ifdef CONFIG_IPC_NS
@@ -200,15 +199,18 @@ void sem_exit_ns(struct ipc_namespace *ns)
{
free_ipcs(ns, &sem_ids(ns), freeary);
idr_destroy(&ns->ids[IPC_SEM_IDS].ipcs_idr);
+ rhashtable_destroy(&ns->ids[IPC_SEM_IDS].key_ht);
}
#endif
-void __init sem_init(void)
+int __init sem_init(void)
{
- sem_init_ns(&init_ipc_ns);
+ const int err = sem_init_ns(&init_ipc_ns);
+
ipc_init_proc_interface("sysvipc/sem",
" key semid perms nsems uid gid cuid cgid otime ctime\n",
IPC_SEM_IDS, sysvipc_sem_proc_show);
+ return err;
}
/**
@@ -511,7 +513,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
INIT_LIST_HEAD(&sma->pending_const);
INIT_LIST_HEAD(&sma->list_id);
sma->sem_nsems = nsems;
- sma->sem_ctime = get_seconds();
+ sma->sem_ctime = ktime_get_real_seconds();
retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
if (retval < 0) {
@@ -1162,14 +1164,14 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in,
}
}
-static time_t get_semotime(struct sem_array *sma)
+static time64_t get_semotime(struct sem_array *sma)
{
int i;
- time_t res;
+ time64_t res;
res = sma->sems[0].sem_otime;
for (i = 1; i < sma->sem_nsems; i++) {
- time_t to = sma->sems[i].sem_otime;
+ time64_t to = sma->sems[i].sem_otime;
if (to > res)
res = to;
@@ -1177,112 +1179,95 @@ static time_t get_semotime(struct sem_array *sma)
return res;
}
-static int semctl_nolock(struct ipc_namespace *ns, int semid,
- int cmd, int version, void __user *p)
+static int semctl_stat(struct ipc_namespace *ns, int semid,
+ int cmd, struct semid64_ds *semid64)
{
- int err;
struct sem_array *sma;
+ int id = 0;
+ int err;
- switch (cmd) {
- case IPC_INFO:
- case SEM_INFO:
- {
- struct seminfo seminfo;
- int max_id;
-
- err = security_sem_semctl(NULL, cmd);
- if (err)
- return err;
+ memset(semid64, 0, sizeof(*semid64));
- memset(&seminfo, 0, sizeof(seminfo));
- seminfo.semmni = ns->sc_semmni;
- seminfo.semmns = ns->sc_semmns;
- seminfo.semmsl = ns->sc_semmsl;
- seminfo.semopm = ns->sc_semopm;
- seminfo.semvmx = SEMVMX;
- seminfo.semmnu = SEMMNU;
- seminfo.semmap = SEMMAP;
- seminfo.semume = SEMUME;
- down_read(&sem_ids(ns).rwsem);
- if (cmd == SEM_INFO) {
- seminfo.semusz = sem_ids(ns).in_use;
- seminfo.semaem = ns->used_sems;
- } else {
- seminfo.semusz = SEMUSZ;
- seminfo.semaem = SEMAEM;
+ rcu_read_lock();
+ if (cmd == SEM_STAT) {
+ sma = sem_obtain_object(ns, semid);
+ if (IS_ERR(sma)) {
+ err = PTR_ERR(sma);
+ goto out_unlock;
+ }
+ id = sma->sem_perm.id;
+ } else {
+ sma = sem_obtain_object_check(ns, semid);
+ if (IS_ERR(sma)) {
+ err = PTR_ERR(sma);
+ goto out_unlock;
}
- max_id = ipc_get_maxid(&sem_ids(ns));
- up_read(&sem_ids(ns).rwsem);
- if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
- return -EFAULT;
- return (max_id < 0) ? 0 : max_id;
}
- case IPC_STAT:
- case SEM_STAT:
- {
- struct semid64_ds tbuf;
- int id = 0;
-
- memset(&tbuf, 0, sizeof(tbuf));
- rcu_read_lock();
- if (cmd == SEM_STAT) {
- sma = sem_obtain_object(ns, semid);
- if (IS_ERR(sma)) {
- err = PTR_ERR(sma);
- goto out_unlock;
- }
- id = sma->sem_perm.id;
- } else {
- sma = sem_obtain_object_check(ns, semid);
- if (IS_ERR(sma)) {
- err = PTR_ERR(sma);
- goto out_unlock;
- }
- }
+ err = -EACCES;
+ if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
+ goto out_unlock;
- err = -EACCES;
- if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
- goto out_unlock;
+ err = security_sem_semctl(sma, cmd);
+ if (err)
+ goto out_unlock;
- err = security_sem_semctl(sma, cmd);
- if (err)
- goto out_unlock;
+ kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm);
+ semid64->sem_otime = get_semotime(sma);
+ semid64->sem_ctime = sma->sem_ctime;
+ semid64->sem_nsems = sma->sem_nsems;
+ rcu_read_unlock();
+ return id;
- kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
- tbuf.sem_otime = get_semotime(sma);
- tbuf.sem_ctime = sma->sem_ctime;
- tbuf.sem_nsems = sma->sem_nsems;
- rcu_read_unlock();
- if (copy_semid_to_user(p, &tbuf, version))
- return -EFAULT;
- return id;
- }
- default:
- return -EINVAL;
- }
out_unlock:
rcu_read_unlock();
return err;
}
+static int semctl_info(struct ipc_namespace *ns, int semid,
+ int cmd, void __user *p)
+{
+ struct seminfo seminfo;
+ int max_id;
+ int err;
+
+ err = security_sem_semctl(NULL, cmd);
+ if (err)
+ return err;
+
+ memset(&seminfo, 0, sizeof(seminfo));
+ seminfo.semmni = ns->sc_semmni;
+ seminfo.semmns = ns->sc_semmns;
+ seminfo.semmsl = ns->sc_semmsl;
+ seminfo.semopm = ns->sc_semopm;
+ seminfo.semvmx = SEMVMX;
+ seminfo.semmnu = SEMMNU;
+ seminfo.semmap = SEMMAP;
+ seminfo.semume = SEMUME;
+ down_read(&sem_ids(ns).rwsem);
+ if (cmd == SEM_INFO) {
+ seminfo.semusz = sem_ids(ns).in_use;
+ seminfo.semaem = ns->used_sems;
+ } else {
+ seminfo.semusz = SEMUSZ;
+ seminfo.semaem = SEMAEM;
+ }
+ max_id = ipc_get_maxid(&sem_ids(ns));
+ up_read(&sem_ids(ns).rwsem);
+ if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
+ return -EFAULT;
+ return (max_id < 0) ? 0 : max_id;
+}
+
static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
- unsigned long arg)
+ int val)
{
struct sem_undo *un;
struct sem_array *sma;
struct sem *curr;
- int err, val;
+ int err;
DEFINE_WAKE_Q(wake_q);
-#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN)
- /* big-endian 64bit */
- val = arg >> 32;
-#else
- /* 32bit or little-endian 64bit */
- val = arg;
-#endif
-
if (val > SEMVMX || val < 0)
return -ERANGE;
@@ -1326,7 +1311,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
curr->semval = val;
curr->sempid = task_tgid_vnr(current);
- sma->sem_ctime = get_seconds();
+ sma->sem_ctime = ktime_get_real_seconds();
/* maybe some queued-up processes were waiting for this */
do_smart_update(sma, NULL, 0, 0, &wake_q);
sem_unlock(sma, -1);
@@ -1454,7 +1439,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
for (i = 0; i < nsems; i++)
un->semadj[i] = 0;
}
- sma->sem_ctime = get_seconds();
+ sma->sem_ctime = ktime_get_real_seconds();
/* maybe some queued-up processes were waiting for this */
do_smart_update(sma, NULL, 0, 0, &wake_q);
err = 0;
@@ -1531,23 +1516,17 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version)
* NOTE: no locks must be held, the rwsem is taken inside this function.
*/
static int semctl_down(struct ipc_namespace *ns, int semid,
- int cmd, int version, void __user *p)
+ int cmd, struct semid64_ds *semid64)
{
struct sem_array *sma;
int err;
- struct semid64_ds semid64;
struct kern_ipc_perm *ipcp;
- if (cmd == IPC_SET) {
- if (copy_semid_from_user(&semid64, p, version))
- return -EFAULT;
- }
-
down_write(&sem_ids(ns).rwsem);
rcu_read_lock();
ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,
- &semid64.sem_perm, 0);
+ &semid64->sem_perm, 0);
if (IS_ERR(ipcp)) {
err = PTR_ERR(ipcp);
goto out_unlock1;
@@ -1567,10 +1546,10 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
goto out_up;
case IPC_SET:
sem_lock(sma, NULL, -1);
- err = ipc_update_perm(&semid64.sem_perm, ipcp);
+ err = ipc_update_perm(&semid64->sem_perm, ipcp);
if (err)
goto out_unlock0;
- sma->sem_ctime = get_seconds();
+ sma->sem_ctime = ktime_get_real_seconds();
break;
default:
err = -EINVAL;
@@ -1591,6 +1570,8 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
int version;
struct ipc_namespace *ns;
void __user *p = (void __user *)arg;
+ struct semid64_ds semid64;
+ int err;
if (semid < 0)
return -EINVAL;
@@ -1601,9 +1582,15 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
switch (cmd) {
case IPC_INFO:
case SEM_INFO:
+ return semctl_info(ns, semid, cmd, p);
case IPC_STAT:
case SEM_STAT:
- return semctl_nolock(ns, semid, cmd, version, p);
+ err = semctl_stat(ns, semid, cmd, &semid64);
+ if (err < 0)
+ return err;
+ if (copy_semid_to_user(p, &semid64, version))
+ err = -EFAULT;
+ return err;
case GETALL:
case GETVAL:
case GETPID:
@@ -1611,15 +1598,120 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
case GETZCNT:
case SETALL:
return semctl_main(ns, semid, semnum, cmd, p);
+ case SETVAL: {
+ int val;
+#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN)
+ /* big-endian 64bit */
+ val = arg >> 32;
+#else
+ /* 32bit or little-endian 64bit */
+ val = arg;
+#endif
+ return semctl_setval(ns, semid, semnum, val);
+ }
+ case IPC_SET:
+ if (copy_semid_from_user(&semid64, p, version))
+ return -EFAULT;
+ case IPC_RMID:
+ return semctl_down(ns, semid, cmd, &semid64);
+ default:
+ return -EINVAL;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+
+struct compat_semid_ds {
+ struct compat_ipc_perm sem_perm;
+ compat_time_t sem_otime;
+ compat_time_t sem_ctime;
+ compat_uptr_t sem_base;
+ compat_uptr_t sem_pending;
+ compat_uptr_t sem_pending_last;
+ compat_uptr_t undo;
+ unsigned short sem_nsems;
+};
+
+static int copy_compat_semid_from_user(struct semid64_ds *out, void __user *buf,
+ int version)
+{
+ memset(out, 0, sizeof(*out));
+ if (version == IPC_64) {
+ struct compat_semid64_ds *p = buf;
+ return get_compat_ipc64_perm(&out->sem_perm, &p->sem_perm);
+ } else {
+ struct compat_semid_ds *p = buf;
+ return get_compat_ipc_perm(&out->sem_perm, &p->sem_perm);
+ }
+}
+
+static int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in,
+ int version)
+{
+ if (version == IPC_64) {
+ struct compat_semid64_ds v;
+ memset(&v, 0, sizeof(v));
+ to_compat_ipc64_perm(&v.sem_perm, &in->sem_perm);
+ v.sem_otime = in->sem_otime;
+ v.sem_ctime = in->sem_ctime;
+ v.sem_nsems = in->sem_nsems;
+ return copy_to_user(buf, &v, sizeof(v));
+ } else {
+ struct compat_semid_ds v;
+ memset(&v, 0, sizeof(v));
+ to_compat_ipc_perm(&v.sem_perm, &in->sem_perm);
+ v.sem_otime = in->sem_otime;
+ v.sem_ctime = in->sem_ctime;
+ v.sem_nsems = in->sem_nsems;
+ return copy_to_user(buf, &v, sizeof(v));
+ }
+}
+
+COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg)
+{
+ void __user *p = compat_ptr(arg);
+ struct ipc_namespace *ns;
+ struct semid64_ds semid64;
+ int version = compat_ipc_parse_version(&cmd);
+ int err;
+
+ ns = current->nsproxy->ipc_ns;
+
+ if (semid < 0)
+ return -EINVAL;
+
+ switch (cmd & (~IPC_64)) {
+ case IPC_INFO:
+ case SEM_INFO:
+ return semctl_info(ns, semid, cmd, p);
+ case IPC_STAT:
+ case SEM_STAT:
+ err = semctl_stat(ns, semid, cmd, &semid64);
+ if (err < 0)
+ return err;
+ if (copy_compat_semid_to_user(p, &semid64, version))
+ err = -EFAULT;
+ return err;
+ case GETVAL:
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ case GETALL:
+ case SETALL:
+ return semctl_main(ns, semid, semnum, cmd, p);
case SETVAL:
return semctl_setval(ns, semid, semnum, arg);
- case IPC_RMID:
case IPC_SET:
- return semctl_down(ns, semid, cmd, version, p);
+ if (copy_compat_semid_from_user(&semid64, p, version))
+ return -EFAULT;
+ /* fallthru */
+ case IPC_RMID:
+ return semctl_down(ns, semid, cmd, &semid64);
default:
return -EINVAL;
}
}
+#endif
/* If the task doesn't already have a undo_list, then allocate one
* here. We guarantee there is only one thread using this undo list,
@@ -1642,7 +1734,7 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp)
if (undo_list == NULL)
return -ENOMEM;
spin_lock_init(&undo_list->lock);
- atomic_set(&undo_list->refcnt, 1);
+ refcount_set(&undo_list->refcnt, 1);
INIT_LIST_HEAD(&undo_list->list_proc);
current->sysvsem.undo_list = undo_list;
@@ -1765,8 +1857,8 @@ out:
return un;
}
-SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
- unsigned, nsops, const struct timespec __user *, timeout)
+static long do_semtimedop(int semid, struct sembuf __user *tsops,
+ unsigned nsops, const struct timespec64 *timeout)
{
int error = -EINVAL;
struct sem_array *sma;
@@ -1786,7 +1878,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
if (nsops > ns->sc_semopm)
return -E2BIG;
if (nsops > SEMOPM_FAST) {
- sops = kmalloc(sizeof(*sops)*nsops, GFP_KERNEL);
+ sops = kvmalloc(sizeof(*sops)*nsops, GFP_KERNEL);
if (sops == NULL)
return -ENOMEM;
}
@@ -1797,17 +1889,12 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
}
if (timeout) {
- struct timespec _timeout;
- if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) {
- error = -EFAULT;
- goto out_free;
- }
- if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
- _timeout.tv_nsec >= 1000000000L) {
+ if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 ||
+ timeout->tv_nsec >= 1000000000L) {
error = -EINVAL;
goto out_free;
}
- jiffies_left = timespec_to_jiffies(&_timeout);
+ jiffies_left = timespec64_to_jiffies(timeout);
}
max = 0;
@@ -2018,14 +2105,41 @@ out_unlock_free:
rcu_read_unlock();
out_free:
if (sops != fast_sops)
- kfree(sops);
+ kvfree(sops);
return error;
}
+SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
+ unsigned, nsops, const struct timespec __user *, timeout)
+{
+ if (timeout) {
+ struct timespec64 ts;
+ if (get_timespec64(&ts, timeout))
+ return -EFAULT;
+ return do_semtimedop(semid, tsops, nsops, &ts);
+ }
+ return do_semtimedop(semid, tsops, nsops, NULL);
+}
+
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems,
+ unsigned, nsops,
+ const struct compat_timespec __user *, timeout)
+{
+ if (timeout) {
+ struct timespec64 ts;
+ if (compat_get_timespec64(&ts, timeout))
+ return -EFAULT;
+ return do_semtimedop(semid, tsems, nsops, &ts);
+ }
+ return do_semtimedop(semid, tsems, nsops, NULL);
+}
+#endif
+
SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,
unsigned, nsops)
{
- return sys_semtimedop(semid, tsops, nsops, NULL);
+ return do_semtimedop(semid, tsops, nsops, NULL);
}
/* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between
@@ -2041,7 +2155,7 @@ int copy_semundo(unsigned long clone_flags, struct task_struct *tsk)
error = get_undo_list(&undo_list);
if (error)
return error;
- atomic_inc(&undo_list->refcnt);
+ refcount_inc(&undo_list->refcnt);
tsk->sysvsem.undo_list = undo_list;
} else
tsk->sysvsem.undo_list = NULL;
@@ -2070,7 +2184,7 @@ void exit_sem(struct task_struct *tsk)
return;
tsk->sysvsem.undo_list = NULL;
- if (!atomic_dec_and_test(&ulp->refcnt))
+ if (!refcount_dec_and_test(&ulp->refcnt))
return;
for (;;) {
@@ -2091,7 +2205,8 @@ void exit_sem(struct task_struct *tsk)
* possibility where we exit while freeary() didn't
* finish unlocking sem_undo_list.
*/
- spin_unlock_wait(&ulp->lock);
+ spin_lock(&ulp->lock);
+ spin_unlock(&ulp->lock);
rcu_read_unlock();
break;
}
@@ -2179,8 +2294,9 @@ void exit_sem(struct task_struct *tsk)
static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
{
struct user_namespace *user_ns = seq_user_ns(s);
- struct sem_array *sma = it;
- time_t sem_otime;
+ struct kern_ipc_perm *ipcp = it;
+ struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
+ time64_t sem_otime;
/*
* The proc interface isn't aware of sem_lock(), it calls
@@ -2193,7 +2309,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
sem_otime = get_semotime(sma);
seq_printf(s,
- "%10d %10d %4o %10u %5u %5u %5u %5u %10lu %10lu\n",
+ "%10d %10d %4o %10u %5u %5u %5u %5u %10llu %10llu\n",
sma->sem_perm.key,
sma->sem_perm.id,
sma->sem_perm.mode,
diff --git a/ipc/shm.c b/ipc/shm.c
index 28a444861a8f..bd652755d32c 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/ipc/shm.c
* Copyright (C) 1992, 1993 Krishna Balasubramanian
@@ -72,14 +73,14 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp);
static int sysvipc_shm_proc_show(struct seq_file *s, void *it);
#endif
-void shm_init_ns(struct ipc_namespace *ns)
+int shm_init_ns(struct ipc_namespace *ns)
{
ns->shm_ctlmax = SHMMAX;
ns->shm_ctlall = SHMALL;
ns->shm_ctlmni = SHMMNI;
ns->shm_rmid_forced = 0;
ns->shm_tot = 0;
- ipc_init_ids(&shm_ids(ns));
+ return ipc_init_ids(&shm_ids(ns));
}
/*
@@ -95,7 +96,7 @@ static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
if (shp->shm_nattch) {
shp->shm_perm.mode |= SHM_DEST;
/* Do not find it any more */
- shp->shm_perm.key = IPC_PRIVATE;
+ ipc_set_key_private(&shm_ids(ns), &shp->shm_perm);
shm_unlock(shp);
} else
shm_destroy(ns, shp);
@@ -106,13 +107,15 @@ void shm_exit_ns(struct ipc_namespace *ns)
{
free_ipcs(ns, &shm_ids(ns), do_shm_rmid);
idr_destroy(&ns->ids[IPC_SHM_IDS].ipcs_idr);
+ rhashtable_destroy(&ns->ids[IPC_SHM_IDS].key_ht);
}
#endif
static int __init ipc_ns_init(void)
{
- shm_init_ns(&init_ipc_ns);
- return 0;
+ const int err = shm_init_ns(&init_ipc_ns);
+ WARN(err, "ipc: sysv shm_init_ns failed: %d\n", err);
+ return err;
}
pure_initcall(ipc_ns_init);
@@ -200,7 +203,7 @@ static int __shm_open(struct vm_area_struct *vma)
if (IS_ERR(shp))
return PTR_ERR(shp);
- shp->shm_atim = get_seconds();
+ shp->shm_atim = ktime_get_real_seconds();
shp->shm_lprid = task_tgid_vnr(current);
shp->shm_nattch++;
shm_unlock(shp);
@@ -287,7 +290,7 @@ static void shm_close(struct vm_area_struct *vma)
goto done; /* no-op */
shp->shm_lprid = task_tgid_vnr(current);
- shp->shm_dtim = get_seconds();
+ shp->shm_dtim = ktime_get_real_seconds();
shp->shm_nattch--;
if (shm_may_destroy(ns, shp))
shm_destroy(ns, shp);
@@ -592,7 +595,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
shp->shm_cprid = task_tgid_vnr(current);
shp->shm_lprid = 0;
shp->shm_atim = shp->shm_dtim = 0;
- shp->shm_ctim = get_seconds();
+ shp->shm_ctim = ktime_get_real_seconds();
shp->shm_segsz = size;
shp->shm_nattch = 0;
shp->shm_file = file;
@@ -813,23 +816,17 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
* NOTE: no locks must be held, the rwsem is taken inside this function.
*/
static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
- struct shmid_ds __user *buf, int version)
+ struct shmid64_ds *shmid64)
{
struct kern_ipc_perm *ipcp;
- struct shmid64_ds shmid64;
struct shmid_kernel *shp;
int err;
- if (cmd == IPC_SET) {
- if (copy_shmid_from_user(&shmid64, buf, version))
- return -EFAULT;
- }
-
down_write(&shm_ids(ns).rwsem);
rcu_read_lock();
ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd,
- &shmid64.shm_perm, 0);
+ &shmid64->shm_perm, 0);
if (IS_ERR(ipcp)) {
err = PTR_ERR(ipcp);
goto out_unlock1;
@@ -849,10 +846,10 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
goto out_up;
case IPC_SET:
ipc_lock_object(&shp->shm_perm);
- err = ipc_update_perm(&shmid64.shm_perm, ipcp);
+ err = ipc_update_perm(&shmid64->shm_perm, ipcp);
if (err)
goto out_unlock0;
- shp->shm_ctim = get_seconds();
+ shp->shm_ctim = ktime_get_real_seconds();
break;
default:
err = -EINVAL;
@@ -868,125 +865,175 @@ out_up:
return err;
}
-static int shmctl_nolock(struct ipc_namespace *ns, int shmid,
- int cmd, int version, void __user *buf)
+static int shmctl_ipc_info(struct ipc_namespace *ns,
+ struct shminfo64 *shminfo)
{
- int err;
- struct shmid_kernel *shp;
-
- /* preliminary security checks for *_INFO */
- if (cmd == IPC_INFO || cmd == SHM_INFO) {
- err = security_shm_shmctl(NULL, cmd);
- if (err)
- return err;
- }
-
- switch (cmd) {
- case IPC_INFO:
- {
- struct shminfo64 shminfo;
-
- memset(&shminfo, 0, sizeof(shminfo));
- shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni;
- shminfo.shmmax = ns->shm_ctlmax;
- shminfo.shmall = ns->shm_ctlall;
-
- shminfo.shmmin = SHMMIN;
- if (copy_shminfo_to_user(buf, &shminfo, version))
- return -EFAULT;
-
+ int err = security_shm_shmctl(NULL, IPC_INFO);
+ if (!err) {
+ memset(shminfo, 0, sizeof(*shminfo));
+ shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni;
+ shminfo->shmmax = ns->shm_ctlmax;
+ shminfo->shmall = ns->shm_ctlall;
+ shminfo->shmmin = SHMMIN;
down_read(&shm_ids(ns).rwsem);
err = ipc_get_maxid(&shm_ids(ns));
up_read(&shm_ids(ns).rwsem);
-
if (err < 0)
err = 0;
- goto out;
}
- case SHM_INFO:
- {
- struct shm_info shm_info;
+ return err;
+}
- memset(&shm_info, 0, sizeof(shm_info));
+static int shmctl_shm_info(struct ipc_namespace *ns,
+ struct shm_info *shm_info)
+{
+ int err = security_shm_shmctl(NULL, SHM_INFO);
+ if (!err) {
+ memset(shm_info, 0, sizeof(*shm_info));
down_read(&shm_ids(ns).rwsem);
- shm_info.used_ids = shm_ids(ns).in_use;
- shm_get_stat(ns, &shm_info.shm_rss, &shm_info.shm_swp);
- shm_info.shm_tot = ns->shm_tot;
- shm_info.swap_attempts = 0;
- shm_info.swap_successes = 0;
+ shm_info->used_ids = shm_ids(ns).in_use;
+ shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp);
+ shm_info->shm_tot = ns->shm_tot;
+ shm_info->swap_attempts = 0;
+ shm_info->swap_successes = 0;
err = ipc_get_maxid(&shm_ids(ns));
up_read(&shm_ids(ns).rwsem);
- if (copy_to_user(buf, &shm_info, sizeof(shm_info))) {
- err = -EFAULT;
- goto out;
+ if (err < 0)
+ err = 0;
+ }
+ return err;
+}
+
+static int shmctl_stat(struct ipc_namespace *ns, int shmid,
+ int cmd, struct shmid64_ds *tbuf)
+{
+ struct shmid_kernel *shp;
+ int result;
+ int err;
+
+ rcu_read_lock();
+ if (cmd == SHM_STAT) {
+ shp = shm_obtain_object(ns, shmid);
+ if (IS_ERR(shp)) {
+ err = PTR_ERR(shp);
+ goto out_unlock;
+ }
+ result = shp->shm_perm.id;
+ } else {
+ shp = shm_obtain_object_check(ns, shmid);
+ if (IS_ERR(shp)) {
+ err = PTR_ERR(shp);
+ goto out_unlock;
}
+ result = 0;
+ }
- err = err < 0 ? 0 : err;
- goto out;
+ err = -EACCES;
+ if (ipcperms(ns, &shp->shm_perm, S_IRUGO))
+ goto out_unlock;
+
+ err = security_shm_shmctl(shp, cmd);
+ if (err)
+ goto out_unlock;
+
+ memset(tbuf, 0, sizeof(*tbuf));
+ kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm);
+ tbuf->shm_segsz = shp->shm_segsz;
+ tbuf->shm_atime = shp->shm_atim;
+ tbuf->shm_dtime = shp->shm_dtim;
+ tbuf->shm_ctime = shp->shm_ctim;
+ tbuf->shm_cpid = shp->shm_cprid;
+ tbuf->shm_lpid = shp->shm_lprid;
+ tbuf->shm_nattch = shp->shm_nattch;
+ rcu_read_unlock();
+ return result;
+
+out_unlock:
+ rcu_read_unlock();
+ return err;
+}
+
+static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd)
+{
+ struct shmid_kernel *shp;
+ struct file *shm_file;
+ int err;
+
+ rcu_read_lock();
+ shp = shm_obtain_object_check(ns, shmid);
+ if (IS_ERR(shp)) {
+ err = PTR_ERR(shp);
+ goto out_unlock1;
}
- case SHM_STAT:
- case IPC_STAT:
- {
- struct shmid64_ds tbuf;
- int result;
-
- rcu_read_lock();
- if (cmd == SHM_STAT) {
- shp = shm_obtain_object(ns, shmid);
- if (IS_ERR(shp)) {
- err = PTR_ERR(shp);
- goto out_unlock;
- }
- result = shp->shm_perm.id;
- } else {
- shp = shm_obtain_object_check(ns, shmid);
- if (IS_ERR(shp)) {
- err = PTR_ERR(shp);
- goto out_unlock;
- }
- result = 0;
- }
- err = -EACCES;
- if (ipcperms(ns, &shp->shm_perm, S_IRUGO))
- goto out_unlock;
+ audit_ipc_obj(&(shp->shm_perm));
+ err = security_shm_shmctl(shp, cmd);
+ if (err)
+ goto out_unlock1;
- err = security_shm_shmctl(shp, cmd);
- if (err)
- goto out_unlock;
+ ipc_lock_object(&shp->shm_perm);
- memset(&tbuf, 0, sizeof(tbuf));
- kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
- tbuf.shm_segsz = shp->shm_segsz;
- tbuf.shm_atime = shp->shm_atim;
- tbuf.shm_dtime = shp->shm_dtim;
- tbuf.shm_ctime = shp->shm_ctim;
- tbuf.shm_cpid = shp->shm_cprid;
- tbuf.shm_lpid = shp->shm_lprid;
- tbuf.shm_nattch = shp->shm_nattch;
- rcu_read_unlock();
-
- if (copy_shmid_to_user(buf, &tbuf, version))
- err = -EFAULT;
- else
- err = result;
- goto out;
+ /* check if shm_destroy() is tearing down shp */
+ if (!ipc_valid_object(&shp->shm_perm)) {
+ err = -EIDRM;
+ goto out_unlock0;
}
- default:
- return -EINVAL;
+
+ if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
+ kuid_t euid = current_euid();
+
+ if (!uid_eq(euid, shp->shm_perm.uid) &&
+ !uid_eq(euid, shp->shm_perm.cuid)) {
+ err = -EPERM;
+ goto out_unlock0;
+ }
+ if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) {
+ err = -EPERM;
+ goto out_unlock0;
+ }
}
-out_unlock:
+ shm_file = shp->shm_file;
+ if (is_file_hugepages(shm_file))
+ goto out_unlock0;
+
+ if (cmd == SHM_LOCK) {
+ struct user_struct *user = current_user();
+
+ err = shmem_lock(shm_file, 1, user);
+ if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) {
+ shp->shm_perm.mode |= SHM_LOCKED;
+ shp->mlock_user = user;
+ }
+ goto out_unlock0;
+ }
+
+ /* SHM_UNLOCK */
+ if (!(shp->shm_perm.mode & SHM_LOCKED))
+ goto out_unlock0;
+ shmem_lock(shm_file, 0, shp->mlock_user);
+ shp->shm_perm.mode &= ~SHM_LOCKED;
+ shp->mlock_user = NULL;
+ get_file(shm_file);
+ ipc_unlock_object(&shp->shm_perm);
+ rcu_read_unlock();
+ shmem_unlock_mapping(shm_file->f_mapping);
+
+ fput(shm_file);
+ return err;
+
+out_unlock0:
+ ipc_unlock_object(&shp->shm_perm);
+out_unlock1:
rcu_read_unlock();
-out:
return err;
}
SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
{
- struct shmid_kernel *shp;
int err, version;
struct ipc_namespace *ns;
+ struct shmid64_ds sem64;
if (cmd < 0 || shmid < 0)
return -EINVAL;
@@ -995,92 +1042,222 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
ns = current->nsproxy->ipc_ns;
switch (cmd) {
- case IPC_INFO:
- case SHM_INFO:
+ case IPC_INFO: {
+ struct shminfo64 shminfo;
+ err = shmctl_ipc_info(ns, &shminfo);
+ if (err < 0)
+ return err;
+ if (copy_shminfo_to_user(buf, &shminfo, version))
+ err = -EFAULT;
+ return err;
+ }
+ case SHM_INFO: {
+ struct shm_info shm_info;
+ err = shmctl_shm_info(ns, &shm_info);
+ if (err < 0)
+ return err;
+ if (copy_to_user(buf, &shm_info, sizeof(shm_info)))
+ err = -EFAULT;
+ return err;
+ }
case SHM_STAT:
- case IPC_STAT:
- return shmctl_nolock(ns, shmid, cmd, version, buf);
- case IPC_RMID:
+ case IPC_STAT: {
+ err = shmctl_stat(ns, shmid, cmd, &sem64);
+ if (err < 0)
+ return err;
+ if (copy_shmid_to_user(buf, &sem64, version))
+ err = -EFAULT;
+ return err;
+ }
case IPC_SET:
- return shmctl_down(ns, shmid, cmd, buf, version);
+ if (copy_shmid_from_user(&sem64, buf, version))
+ return -EFAULT;
+ /* fallthru */
+ case IPC_RMID:
+ return shmctl_down(ns, shmid, cmd, &sem64);
case SHM_LOCK:
case SHM_UNLOCK:
- {
- struct file *shm_file;
+ return shmctl_do_lock(ns, shmid, cmd);
+ default:
+ return -EINVAL;
+ }
+}
- rcu_read_lock();
- shp = shm_obtain_object_check(ns, shmid);
- if (IS_ERR(shp)) {
- err = PTR_ERR(shp);
- goto out_unlock1;
- }
+#ifdef CONFIG_COMPAT
+
+struct compat_shmid_ds {
+ struct compat_ipc_perm shm_perm;
+ int shm_segsz;
+ compat_time_t shm_atime;
+ compat_time_t shm_dtime;
+ compat_time_t shm_ctime;
+ compat_ipc_pid_t shm_cpid;
+ compat_ipc_pid_t shm_lpid;
+ unsigned short shm_nattch;
+ unsigned short shm_unused;
+ compat_uptr_t shm_unused2;
+ compat_uptr_t shm_unused3;
+};
- audit_ipc_obj(&(shp->shm_perm));
- err = security_shm_shmctl(shp, cmd);
- if (err)
- goto out_unlock1;
+struct compat_shminfo64 {
+ compat_ulong_t shmmax;
+ compat_ulong_t shmmin;
+ compat_ulong_t shmmni;
+ compat_ulong_t shmseg;
+ compat_ulong_t shmall;
+ compat_ulong_t __unused1;
+ compat_ulong_t __unused2;
+ compat_ulong_t __unused3;
+ compat_ulong_t __unused4;
+};
- ipc_lock_object(&shp->shm_perm);
+struct compat_shm_info {
+ compat_int_t used_ids;
+ compat_ulong_t shm_tot, shm_rss, shm_swp;
+ compat_ulong_t swap_attempts, swap_successes;
+};
- /* check if shm_destroy() is tearing down shp */
- if (!ipc_valid_object(&shp->shm_perm)) {
- err = -EIDRM;
- goto out_unlock0;
- }
+static int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in,
+ int version)
+{
+ if (in->shmmax > INT_MAX)
+ in->shmmax = INT_MAX;
+ if (version == IPC_64) {
+ struct compat_shminfo64 info;
+ memset(&info, 0, sizeof(info));
+ info.shmmax = in->shmmax;
+ info.shmmin = in->shmmin;
+ info.shmmni = in->shmmni;
+ info.shmseg = in->shmseg;
+ info.shmall = in->shmall;
+ return copy_to_user(buf, &info, sizeof(info));
+ } else {
+ struct shminfo info;
+ memset(&info, 0, sizeof(info));
+ info.shmmax = in->shmmax;
+ info.shmmin = in->shmmin;
+ info.shmmni = in->shmmni;
+ info.shmseg = in->shmseg;
+ info.shmall = in->shmall;
+ return copy_to_user(buf, &info, sizeof(info));
+ }
+}
- if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
- kuid_t euid = current_euid();
-
- if (!uid_eq(euid, shp->shm_perm.uid) &&
- !uid_eq(euid, shp->shm_perm.cuid)) {
- err = -EPERM;
- goto out_unlock0;
- }
- if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) {
- err = -EPERM;
- goto out_unlock0;
- }
- }
+static int put_compat_shm_info(struct shm_info *ip,
+ struct compat_shm_info __user *uip)
+{
+ struct compat_shm_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.used_ids = ip->used_ids;
+ info.shm_tot = ip->shm_tot;
+ info.shm_rss = ip->shm_rss;
+ info.shm_swp = ip->shm_swp;
+ info.swap_attempts = ip->swap_attempts;
+ info.swap_successes = ip->swap_successes;
+ return copy_to_user(uip, &info, sizeof(info));
+}
- shm_file = shp->shm_file;
- if (is_file_hugepages(shm_file))
- goto out_unlock0;
+static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in,
+ int version)
+{
+ if (version == IPC_64) {
+ struct compat_shmid64_ds v;
+ memset(&v, 0, sizeof(v));
+ to_compat_ipc64_perm(&v.shm_perm, &in->shm_perm);
+ v.shm_atime = in->shm_atime;
+ v.shm_dtime = in->shm_dtime;
+ v.shm_ctime = in->shm_ctime;
+ v.shm_segsz = in->shm_segsz;
+ v.shm_nattch = in->shm_nattch;
+ v.shm_cpid = in->shm_cpid;
+ v.shm_lpid = in->shm_lpid;
+ return copy_to_user(buf, &v, sizeof(v));
+ } else {
+ struct compat_shmid_ds v;
+ memset(&v, 0, sizeof(v));
+ to_compat_ipc_perm(&v.shm_perm, &in->shm_perm);
+ v.shm_perm.key = in->shm_perm.key;
+ v.shm_atime = in->shm_atime;
+ v.shm_dtime = in->shm_dtime;
+ v.shm_ctime = in->shm_ctime;
+ v.shm_segsz = in->shm_segsz;
+ v.shm_nattch = in->shm_nattch;
+ v.shm_cpid = in->shm_cpid;
+ v.shm_lpid = in->shm_lpid;
+ return copy_to_user(buf, &v, sizeof(v));
+ }
+}
- if (cmd == SHM_LOCK) {
- struct user_struct *user = current_user();
+static int copy_compat_shmid_from_user(struct shmid64_ds *out, void __user *buf,
+ int version)
+{
+ memset(out, 0, sizeof(*out));
+ if (version == IPC_64) {
+ struct compat_shmid64_ds *p = buf;
+ return get_compat_ipc64_perm(&out->shm_perm, &p->shm_perm);
+ } else {
+ struct compat_shmid_ds *p = buf;
+ return get_compat_ipc_perm(&out->shm_perm, &p->shm_perm);
+ }
+}
- err = shmem_lock(shm_file, 1, user);
- if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) {
- shp->shm_perm.mode |= SHM_LOCKED;
- shp->mlock_user = user;
- }
- goto out_unlock0;
- }
+COMPAT_SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, void __user *, uptr)
+{
+ struct ipc_namespace *ns;
+ struct shmid64_ds sem64;
+ int version = compat_ipc_parse_version(&cmd);
+ int err;
- /* SHM_UNLOCK */
- if (!(shp->shm_perm.mode & SHM_LOCKED))
- goto out_unlock0;
- shmem_lock(shm_file, 0, shp->mlock_user);
- shp->shm_perm.mode &= ~SHM_LOCKED;
- shp->mlock_user = NULL;
- get_file(shm_file);
- ipc_unlock_object(&shp->shm_perm);
- rcu_read_unlock();
- shmem_unlock_mapping(shm_file->f_mapping);
+ ns = current->nsproxy->ipc_ns;
- fput(shm_file);
+ if (cmd < 0 || shmid < 0)
+ return -EINVAL;
+
+ switch (cmd) {
+ case IPC_INFO: {
+ struct shminfo64 shminfo;
+ err = shmctl_ipc_info(ns, &shminfo);
+ if (err < 0)
+ return err;
+ if (copy_compat_shminfo_to_user(uptr, &shminfo, version))
+ err = -EFAULT;
+ return err;
+ }
+ case SHM_INFO: {
+ struct shm_info shm_info;
+ err = shmctl_shm_info(ns, &shm_info);
+ if (err < 0)
+ return err;
+ if (put_compat_shm_info(&shm_info, uptr))
+ err = -EFAULT;
return err;
}
+ case IPC_STAT:
+ case SHM_STAT:
+ err = shmctl_stat(ns, shmid, cmd, &sem64);
+ if (err < 0)
+ return err;
+ if (copy_compat_shmid_to_user(uptr, &sem64, version))
+ err = -EFAULT;
+ return err;
+
+ case IPC_SET:
+ if (copy_compat_shmid_from_user(&sem64, uptr, version))
+ return -EFAULT;
+ /* fallthru */
+ case IPC_RMID:
+ return shmctl_down(ns, shmid, cmd, &sem64);
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ return shmctl_do_lock(ns, shmid, cmd);
+ break;
default:
return -EINVAL;
}
-
-out_unlock0:
- ipc_unlock_object(&shp->shm_perm);
-out_unlock1:
- rcu_read_unlock();
return err;
}
+#endif
/*
* Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
@@ -1265,6 +1442,25 @@ SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg)
return (long)ret;
}
+#ifdef CONFIG_COMPAT
+
+#ifndef COMPAT_SHMLBA
+#define COMPAT_SHMLBA SHMLBA
+#endif
+
+COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg)
+{
+ unsigned long ret;
+ long err;
+
+ err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA);
+ if (err)
+ return err;
+ force_successful_syscall_return();
+ return (long)ret;
+}
+#endif
+
/*
* detach and kill segment if marked destroyed.
* The work is done in shm_close.
@@ -1380,9 +1576,11 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr)
static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
{
struct user_namespace *user_ns = seq_user_ns(s);
- struct shmid_kernel *shp = it;
+ struct kern_ipc_perm *ipcp = it;
+ struct shmid_kernel *shp;
unsigned long rss = 0, swp = 0;
+ shp = container_of(ipcp, struct shmid_kernel, shm_perm);
shm_add_rss_swap(shp, &rss, &swp);
#if BITS_PER_LONG <= 32
@@ -1393,7 +1591,7 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
seq_printf(s,
"%10d %10d %4o " SIZE_SPEC " %5u %5u "
- "%5lu %5u %5u %5u %5u %10lu %10lu %10lu "
+ "%5lu %5u %5u %5u %5u %10llu %10llu %10llu "
SIZE_SPEC " " SIZE_SPEC "\n",
shp->shm_perm.key,
shp->shm_perm.id,
diff --git a/ipc/syscall.c b/ipc/syscall.c
index 52429489cde0..26b45db2e007 100644
--- a/ipc/syscall.c
+++ b/ipc/syscall.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* sys_ipc() is the old de-multiplexer for the SysV IPC calls.
*
@@ -5,12 +6,12 @@
* the individual syscalls instead.
*/
#include <linux/unistd.h>
+#include <linux/syscalls.h>
#ifdef __ARCH_WANT_SYS_IPC
#include <linux/errno.h>
#include <linux/ipc.h>
#include <linux/shm.h>
-#include <linux/syscalls.h>
#include <linux/uaccess.h>
SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second,
@@ -97,3 +98,91 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second,
}
}
#endif
+
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+
+#ifndef COMPAT_SHMLBA
+#define COMPAT_SHMLBA SHMLBA
+#endif
+
+struct compat_ipc_kludge {
+ compat_uptr_t msgp;
+ compat_long_t msgtyp;
+};
+
+#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
+COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second,
+ u32, third, compat_uptr_t, ptr, u32, fifth)
+{
+ int version;
+ u32 pad;
+
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+
+ switch (call) {
+ case SEMOP:
+ /* struct sembuf is the same on 32 and 64bit :)) */
+ return sys_semtimedop(first, compat_ptr(ptr), second, NULL);
+ case SEMTIMEDOP:
+ return compat_sys_semtimedop(first, compat_ptr(ptr), second,
+ compat_ptr(fifth));
+ case SEMGET:
+ return sys_semget(first, second, third);
+ case SEMCTL:
+ if (!ptr)
+ return -EINVAL;
+ if (get_user(pad, (u32 __user *) compat_ptr(ptr)))
+ return -EFAULT;
+ return compat_sys_semctl(first, second, third, pad);
+
+ case MSGSND:
+ return compat_sys_msgsnd(first, ptr, second, third);
+
+ case MSGRCV: {
+ void __user *uptr = compat_ptr(ptr);
+
+ if (first < 0 || second < 0)
+ return -EINVAL;
+
+ if (!version) {
+ struct compat_ipc_kludge ipck;
+ if (!uptr)
+ return -EINVAL;
+ if (copy_from_user(&ipck, uptr, sizeof(ipck)))
+ return -EFAULT;
+ return compat_sys_msgrcv(first, ipck.msgp, second,
+ ipck.msgtyp, third);
+ }
+ return compat_sys_msgrcv(first, ptr, second, fifth, third);
+ }
+ case MSGGET:
+ return sys_msgget(first, second);
+ case MSGCTL:
+ return compat_sys_msgctl(first, second, compat_ptr(ptr));
+
+ case SHMAT: {
+ int err;
+ unsigned long raddr;
+
+ if (version == 1)
+ return -EINVAL;
+ err = do_shmat(first, compat_ptr(ptr), second, &raddr,
+ COMPAT_SHMLBA);
+ if (err < 0)
+ return err;
+ return put_user(raddr, (compat_ulong_t *)compat_ptr(third));
+ }
+ case SHMDT:
+ return sys_shmdt(compat_ptr(ptr));
+ case SHMGET:
+ return sys_shmget(first, (unsigned)second, third);
+ case SHMCTL:
+ return compat_sys_shmctl(first, second, compat_ptr(ptr));
+ }
+
+ return -ENOSYS;
+}
+#endif
+#endif
diff --git a/ipc/util.c b/ipc/util.c
index 1a2cb02467ab..79b30eee32cd 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/ipc/util.c
* Copyright (C) 1992 Krishna Balasubramanian
@@ -83,27 +84,46 @@ struct ipc_proc_iface {
*/
static int __init ipc_init(void)
{
- sem_init();
- msg_init();
+ int err_sem, err_msg;
+
+ err_sem = sem_init();
+ WARN(err_sem, "ipc: sysv sem_init failed: %d\n", err_sem);
+ err_msg = msg_init();
+ WARN(err_msg, "ipc: sysv msg_init failed: %d\n", err_msg);
shm_init();
- return 0;
+
+ return err_msg ? err_msg : err_sem;
}
device_initcall(ipc_init);
+static const struct rhashtable_params ipc_kht_params = {
+ .head_offset = offsetof(struct kern_ipc_perm, khtnode),
+ .key_offset = offsetof(struct kern_ipc_perm, key),
+ .key_len = FIELD_SIZEOF(struct kern_ipc_perm, key),
+ .locks_mul = 1,
+ .automatic_shrinking = true,
+};
+
/**
* ipc_init_ids - initialise ipc identifiers
* @ids: ipc identifier set
*
* Set up the sequence range to use for the ipc identifier range (limited
- * below IPCMNI) then initialise the ids idr.
+ * below IPCMNI) then initialise the keys hashtable and ids idr.
*/
-void ipc_init_ids(struct ipc_ids *ids)
+int ipc_init_ids(struct ipc_ids *ids)
{
+ int err;
ids->in_use = 0;
ids->seq = 0;
ids->next_id = -1;
init_rwsem(&ids->rwsem);
+ err = rhashtable_init(&ids->key_ht, &ipc_kht_params);
+ if (err)
+ return err;
idr_init(&ids->ipcs_idr);
+ ids->tables_initialized = true;
+ return 0;
}
#ifdef CONFIG_PROC_FS
@@ -147,28 +167,20 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
* Returns the locked pointer to the ipc structure if found or NULL
* otherwise. If key is found ipc points to the owning ipc structure
*
- * Called with ipc_ids.rwsem held.
+ * Called with writer ipc_ids.rwsem held.
*/
static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
{
- struct kern_ipc_perm *ipc;
- int next_id;
- int total;
-
- for (total = 0, next_id = 0; total < ids->in_use; next_id++) {
- ipc = idr_find(&ids->ipcs_idr, next_id);
-
- if (ipc == NULL)
- continue;
+ struct kern_ipc_perm *ipcp = NULL;
- if (ipc->key != key) {
- total++;
- continue;
- }
+ if (likely(ids->tables_initialized))
+ ipcp = rhashtable_lookup_fast(&ids->key_ht, &key,
+ ipc_kht_params);
+ if (ipcp) {
rcu_read_lock();
- ipc_lock_object(ipc);
- return ipc;
+ ipc_lock_object(ipcp);
+ return ipcp;
}
return NULL;
@@ -221,18 +233,18 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
{
kuid_t euid;
kgid_t egid;
- int id;
+ int id, err;
int next_id = ids->next_id;
if (size > IPCMNI)
size = IPCMNI;
- if (ids->in_use >= size)
+ if (!ids->tables_initialized || ids->in_use >= size)
return -ENOSPC;
idr_preload(GFP_KERNEL);
- atomic_set(&new->refcount, 1);
+ refcount_set(&new->refcount, 1);
spin_lock_init(&new->lock);
new->deleted = false;
rcu_read_lock();
@@ -246,6 +258,15 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
(next_id < 0) ? 0 : ipcid_to_idx(next_id), 0,
GFP_NOWAIT);
idr_preload_end();
+
+ if (id >= 0 && new->key != IPC_PRIVATE) {
+ err = rhashtable_insert_fast(&ids->key_ht, &new->khtnode,
+ ipc_kht_params);
+ if (err < 0) {
+ idr_remove(&ids->ipcs_idr, id);
+ id = err;
+ }
+ }
if (id < 0) {
spin_unlock(&new->lock);
rcu_read_unlock();
@@ -377,6 +398,20 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
return err;
}
+/**
+ * ipc_kht_remove - remove an ipc from the key hashtable
+ * @ids: ipc identifier set
+ * @ipcp: ipc perm structure containing the key to remove
+ *
+ * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
+ * before this function is called, and remain locked on the exit.
+ */
+static void ipc_kht_remove(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
+{
+ if (ipcp->key != IPC_PRIVATE)
+ rhashtable_remove_fast(&ids->key_ht, &ipcp->khtnode,
+ ipc_kht_params);
+}
/**
* ipc_rmid - remove an ipc identifier
@@ -391,19 +426,34 @@ void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
int lid = ipcid_to_idx(ipcp->id);
idr_remove(&ids->ipcs_idr, lid);
+ ipc_kht_remove(ids, ipcp);
ids->in_use--;
ipcp->deleted = true;
}
+/**
+ * ipc_set_key_private - switch the key of an existing ipc to IPC_PRIVATE
+ * @ids: ipc identifier set
+ * @ipcp: ipc perm structure containing the key to modify
+ *
+ * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
+ * before this function is called, and remain locked on the exit.
+ */
+void ipc_set_key_private(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
+{
+ ipc_kht_remove(ids, ipcp);
+ ipcp->key = IPC_PRIVATE;
+}
+
int ipc_rcu_getref(struct kern_ipc_perm *ptr)
{
- return atomic_inc_not_zero(&ptr->refcount);
+ return refcount_inc_not_zero(&ptr->refcount);
}
void ipc_rcu_putref(struct kern_ipc_perm *ptr,
void (*func)(struct rcu_head *head))
{
- if (!atomic_dec_and_test(&ptr->refcount))
+ if (!refcount_dec_and_test(&ptr->refcount))
return;
call_rcu(&ptr->rcu, func);
@@ -485,7 +535,7 @@ void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out)
}
/**
- * ipc_obtain_object
+ * ipc_obtain_object_idr
* @ids: ipc identifier set
* @id: ipc id to look for
*
@@ -499,6 +549,9 @@ struct kern_ipc_perm *ipc_obtain_object_idr(struct ipc_ids *ids, int id)
struct kern_ipc_perm *out;
int lid = ipcid_to_idx(id);
+ if (unlikely(!ids->tables_initialized))
+ return ERR_PTR(-EINVAL);
+
out = idr_find(&ids->ipcs_idr, lid);
if (!out)
return ERR_PTR(-EINVAL);
diff --git a/ipc/util.h b/ipc/util.h
index c692010e6f0a..579112d90016 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* linux/ipc/util.h
* Copyright (C) 1999 Christoph Rohland
@@ -15,8 +16,8 @@
#define SEQ_MULTIPLIER (IPCMNI)
-void sem_init(void);
-void msg_init(void);
+int sem_init(void);
+int msg_init(void);
void shm_init(void);
struct ipc_namespace;
@@ -30,17 +31,17 @@ static inline void mq_put_mnt(struct ipc_namespace *ns) { }
#endif
#ifdef CONFIG_SYSVIPC
-void sem_init_ns(struct ipc_namespace *ns);
-void msg_init_ns(struct ipc_namespace *ns);
-void shm_init_ns(struct ipc_namespace *ns);
+int sem_init_ns(struct ipc_namespace *ns);
+int msg_init_ns(struct ipc_namespace *ns);
+int shm_init_ns(struct ipc_namespace *ns);
void sem_exit_ns(struct ipc_namespace *ns);
void msg_exit_ns(struct ipc_namespace *ns);
void shm_exit_ns(struct ipc_namespace *ns);
#else
-static inline void sem_init_ns(struct ipc_namespace *ns) { }
-static inline void msg_init_ns(struct ipc_namespace *ns) { }
-static inline void shm_init_ns(struct ipc_namespace *ns) { }
+static inline int sem_init_ns(struct ipc_namespace *ns) { return 0; }
+static inline int msg_init_ns(struct ipc_namespace *ns) { return 0; }
+static inline int shm_init_ns(struct ipc_namespace *ns) { return 0; }
static inline void sem_exit_ns(struct ipc_namespace *ns) { }
static inline void msg_exit_ns(struct ipc_namespace *ns) { }
@@ -79,7 +80,7 @@ struct ipc_ops {
struct seq_file;
struct ipc_ids;
-void ipc_init_ids(struct ipc_ids *);
+int ipc_init_ids(struct ipc_ids *);
#ifdef CONFIG_PROC_FS
void __init ipc_init_proc_interface(const char *path, const char *header,
int ids, int (*show)(struct seq_file *, void *));
@@ -104,6 +105,9 @@ int ipc_get_maxid(struct ipc_ids *);
/* must be called with both locks acquired. */
void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *);
+/* must be called with both locks acquired. */
+void ipc_set_key_private(struct ipc_ids *, struct kern_ipc_perm *);
+
/* must be called with ipcp locked */
int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg);
@@ -191,4 +195,34 @@ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
const struct ipc_ops *ops, struct ipc_params *params);
void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
void (*free)(struct ipc_namespace *, struct kern_ipc_perm *));
+
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+struct compat_ipc_perm {
+ key_t key;
+ __compat_uid_t uid;
+ __compat_gid_t gid;
+ __compat_uid_t cuid;
+ __compat_gid_t cgid;
+ compat_mode_t mode;
+ unsigned short seq;
+};
+
+void to_compat_ipc_perm(struct compat_ipc_perm *, struct ipc64_perm *);
+void to_compat_ipc64_perm(struct compat_ipc64_perm *, struct ipc64_perm *);
+int get_compat_ipc_perm(struct ipc64_perm *, struct compat_ipc_perm __user *);
+int get_compat_ipc64_perm(struct ipc64_perm *,
+ struct compat_ipc64_perm __user *);
+
+static inline int compat_ipc_parse_version(int *cmd)
+{
+#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION
+ int version = *cmd & IPC_64;
+ *cmd &= ~IPC_64;
+ return version;
+#else
+ return IPC_64;
+#endif
+}
+#endif
#endif