From 553f770ef71b27ee053bd241bef0998a15f43467 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 8 Jul 2017 22:52:47 -0400 Subject: [PATCH] ipc: move compat shmctl to native Signed-off-by: Al Viro --- ipc/compat.c | 233 +-------------------------------------------------- ipc/shm.c | 209 +++++++++++++++++++++++++++++++++++++++++++-- ipc/util.h | 24 ++++++ 3 files changed, 231 insertions(+), 235 deletions(-) diff --git a/ipc/compat.c b/ipc/compat.c index 9b3c85f8a538..fbfd6fb0a68d 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -39,16 +39,6 @@ struct compat_msgbuf { 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; @@ -76,44 +66,12 @@ struct compat_msqid_ds { 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) +static inline int __compat_ipc_parse_version(int *cmd) { #ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION int version = *cmd & IPC_64; @@ -241,7 +199,7 @@ static long do_compat_semctl(int first, int second, int third, u32 pad) int err, err2; struct semid64_ds sem64; struct semid64_ds __user *up64; - int version = compat_ipc_parse_version(&third); + int version = __compat_ipc_parse_version(&third); memset(&sem64, 0, sizeof(sem64)); @@ -499,7 +457,7 @@ 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); + int version = __compat_ipc_parse_version(&second); void __user *p; memset(&m64, 0, sizeof(m64)); @@ -561,191 +519,6 @@ COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg) 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) diff --git a/ipc/shm.c b/ipc/shm.c index b4073c08d0e8..87334ee3acb3 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1030,7 +1030,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) { int err, version; struct ipc_namespace *ns; - struct shmid64_ds tbuf; + struct shmid64_ds sem64; if (cmd < 0 || shmid < 0) return -EINVAL; @@ -1059,18 +1059,19 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) } case SHM_STAT: case IPC_STAT: { - err = shmctl_stat(ns, shmid, cmd, &tbuf); + err = shmctl_stat(ns, shmid, cmd, &sem64); if (err < 0) return err; - if (copy_shmid_to_user(buf, &tbuf, version)) + if (copy_shmid_to_user(buf, &sem64, version)) err = -EFAULT; return err; } case IPC_SET: - if (copy_shmid_from_user(&tbuf, buf, version)) + if (copy_shmid_from_user(&sem64, buf, version)) return -EFAULT; + /* fallthru */ case IPC_RMID: - return shmctl_down(ns, shmid, cmd, &tbuf); + return shmctl_down(ns, shmid, cmd, &sem64); case SHM_LOCK: case SHM_UNLOCK: return shmctl_do_lock(ns, shmid, cmd); @@ -1079,6 +1080,204 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) } } +#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; +}; + +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 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)); + } +} + +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(up, &info, sizeof(info)); +} + +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)); + v.shm_perm.key = in->shm_perm.key; + v.shm_perm.uid = in->shm_perm.uid; + v.shm_perm.gid = in->shm_perm.gid; + v.shm_perm.cuid = in->shm_perm.cuid; + v.shm_perm.cgid = in->shm_perm.cgid; + v.shm_perm.mode = in->shm_perm.mode; + v.shm_perm.seq = in->shm_perm.seq; + 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)); + v.shm_perm.key = in->shm_perm.key; + SET_UID(v.shm_perm.uid, in->shm_perm.uid); + SET_GID(v.shm_perm.gid, in->shm_perm.gid); + SET_UID(v.shm_perm.cuid, in->shm_perm.cuid); + SET_GID(v.shm_perm.cgid, in->shm_perm.cgid); + v.shm_perm.mode = in->shm_perm.mode; + v.shm_perm.seq = in->shm_perm.seq; + 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)); + } +} + +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; + struct compat_ipc64_perm v; + if (copy_from_user(&v, &p->shm_perm, sizeof(v))) + return -EFAULT; + out->shm_perm.uid = v.uid; + out->shm_perm.gid = v.gid; + out->shm_perm.mode = v.mode; + } else { + struct compat_shmid_ds *p = buf; + struct compat_ipc_perm v; + if (copy_from_user(&v, &p->shm_perm, sizeof(v))) + return -EFAULT; + out->shm_perm.uid = v.uid; + out->shm_perm.gid = v.gid; + out->shm_perm.mode = v.mode; + } + return 0; +} + +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; + + ns = current->nsproxy->ipc_ns; + + 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(&sem64, uptr, 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; + } + return err; +} +#endif + /* * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. * diff --git a/ipc/util.h b/ipc/util.h index c692010e6f0a..3a3dfe137bee 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -191,4 +191,28 @@ 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 +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; +}; + +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