From 9ba720c18622b250c0abeccbcea1b03531a92277 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 8 Jul 2017 20:58:06 -0400 Subject: shmctl: split the work from copyin/copyout Signed-off-by: Al Viro --- ipc/shm.c | 347 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 172 insertions(+), 175 deletions(-) diff --git a/ipc/shm.c b/ipc/shm.c index 28a444861a8f..b4073c08d0e8 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -813,23 +813,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,7 +843,7 @@ 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(); @@ -868,212 +862,162 @@ 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; - } - - err = err < 0 ? 0 : err; - goto out; + if (err < 0) + err = 0; } - 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; - } + return err; +} - err = -EACCES; - if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) - goto out_unlock; +static int shmctl_stat(struct ipc_namespace *ns, int shmid, + int cmd, struct shmid64_ds *tbuf) +{ + struct shmid_kernel *shp; + int result; + int err; - err = security_shm_shmctl(shp, cmd); - if (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; + } - 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(); + err = -EACCES; + if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) + goto out_unlock; - if (copy_shmid_to_user(buf, &tbuf, version)) - err = -EFAULT; - else - err = result; - goto out; - } - default: - return -EINVAL; - } + 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(); -out: return err; } -SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) +static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd) { struct shmid_kernel *shp; - int err, version; - struct ipc_namespace *ns; - - if (cmd < 0 || shmid < 0) - return -EINVAL; + struct file *shm_file; + int err; - version = ipc_parse_version(&cmd); - ns = current->nsproxy->ipc_ns; + rcu_read_lock(); + shp = shm_obtain_object_check(ns, shmid); + if (IS_ERR(shp)) { + err = PTR_ERR(shp); + goto out_unlock1; + } - switch (cmd) { - case IPC_INFO: - case SHM_INFO: - case SHM_STAT: - case IPC_STAT: - return shmctl_nolock(ns, shmid, cmd, version, buf); - case IPC_RMID: - case IPC_SET: - return shmctl_down(ns, shmid, cmd, buf, version); - case SHM_LOCK: - case SHM_UNLOCK: - { - struct file *shm_file; + audit_ipc_obj(&(shp->shm_perm)); + err = security_shm_shmctl(shp, cmd); + if (err) + goto out_unlock1; - rcu_read_lock(); - shp = shm_obtain_object_check(ns, shmid); - if (IS_ERR(shp)) { - err = PTR_ERR(shp); - goto out_unlock1; - } + ipc_lock_object(&shp->shm_perm); - audit_ipc_obj(&(shp->shm_perm)); - err = security_shm_shmctl(shp, cmd); - if (err) - goto out_unlock1; + /* check if shm_destroy() is tearing down shp */ + if (!ipc_valid_object(&shp->shm_perm)) { + err = -EIDRM; + goto out_unlock0; + } - ipc_lock_object(&shp->shm_perm); + if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { + kuid_t euid = current_euid(); - /* check if shm_destroy() is tearing down shp */ - if (!ipc_valid_object(&shp->shm_perm)) { - err = -EIDRM; + if (!uid_eq(euid, shp->shm_perm.uid) && + !uid_eq(euid, shp->shm_perm.cuid)) { + err = -EPERM; goto out_unlock0; } - - 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; - } + if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { + err = -EPERM; + goto out_unlock0; } + } - shm_file = shp->shm_file; - if (is_file_hugepages(shm_file)) - goto out_unlock0; + shm_file = shp->shm_file; + if (is_file_hugepages(shm_file)) + goto out_unlock0; - if (cmd == SHM_LOCK) { - struct user_struct *user = current_user(); + 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; + 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); + /* 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; - } - default: - return -EINVAL; - } + fput(shm_file); + return err; out_unlock0: ipc_unlock_object(&shp->shm_perm); @@ -1082,6 +1026,59 @@ out_unlock1: return err; } +SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) +{ + int err, version; + struct ipc_namespace *ns; + struct shmid64_ds tbuf; + + if (cmd < 0 || shmid < 0) + return -EINVAL; + + version = ipc_parse_version(&cmd); + ns = current->nsproxy->ipc_ns; + + switch (cmd) { + 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: { + err = shmctl_stat(ns, shmid, cmd, &tbuf); + if (err < 0) + return err; + if (copy_shmid_to_user(buf, &tbuf, version)) + err = -EFAULT; + return err; + } + case IPC_SET: + if (copy_shmid_from_user(&tbuf, buf, version)) + return -EFAULT; + case IPC_RMID: + return shmctl_down(ns, shmid, cmd, &tbuf); + case SHM_LOCK: + case SHM_UNLOCK: + return shmctl_do_lock(ns, shmid, cmd); + default: + return -EINVAL; + } +} + /* * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. * -- cgit v1.2.3-58-ga151 From 553f770ef71b27ee053bd241bef0998a15f43467 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 8 Jul 2017 22:52:47 -0400 Subject: 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 -- cgit v1.2.3-58-ga151 From 156d9ed1260ee566f2be09c13254b58247edfb29 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 07:57:34 -0400 Subject: msgctl(): split the actual work from copyin/copyout Signed-off-by: Al Viro --- ipc/msg.c | 202 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 96 insertions(+), 106 deletions(-) diff --git a/ipc/msg.c b/ipc/msg.c index 5b25e0755656..322e7bf8b8d1 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -361,23 +361,17 @@ copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version) * NOTE: no locks must be held, the rwsem is taken inside this function. */ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, - struct msqid_ds __user *buf, int version) + struct msqid64_ds *msqid64) { struct kern_ipc_perm *ipcp; - struct msqid64_ds uninitialized_var(msqid64); struct msg_queue *msq; int err; - if (cmd == IPC_SET) { - if (copy_msqid_from_user(&msqid64, buf, version)) - return -EFAULT; - } - down_write(&msg_ids(ns).rwsem); rcu_read_lock(); ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd, - &msqid64.msg_perm, msqid64.msg_qbytes); + &msqid64->msg_perm, msqid64->msg_qbytes); if (IS_ERR(ipcp)) { err = PTR_ERR(ipcp); goto out_unlock1; @@ -399,18 +393,18 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, { DEFINE_WAKE_Q(wake_q); - if (msqid64.msg_qbytes > ns->msg_ctlmnb && + if (msqid64->msg_qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) { err = -EPERM; goto out_unlock1; } ipc_lock_object(&msq->q_perm); - err = ipc_update_perm(&msqid64.msg_perm, ipcp); + err = ipc_update_perm(&msqid64->msg_perm, ipcp); if (err) goto out_unlock0; - msq->q_qbytes = msqid64.msg_qbytes; + msq->q_qbytes = msqid64->msg_qbytes; msq->q_ctime = get_seconds(); /* @@ -442,111 +436,89 @@ out_up: return err; } -static int msgctl_nolock(struct ipc_namespace *ns, int msqid, - int cmd, int version, void __user *buf) +static int msgctl_info(struct ipc_namespace *ns, int msqid, + int cmd, struct msginfo *msginfo) { int err; - struct msg_queue *msq; - - switch (cmd) { - case IPC_INFO: - case MSG_INFO: - { - struct msginfo msginfo; - int max_id; - - if (!buf) - return -EFAULT; + int max_id; - /* - * We must not return kernel stack data. - * due to padding, it's not enough - * to set all member fields. - */ - err = security_msg_queue_msgctl(NULL, cmd); - if (err) - return err; - - memset(&msginfo, 0, sizeof(msginfo)); - msginfo.msgmni = ns->msg_ctlmni; - msginfo.msgmax = ns->msg_ctlmax; - msginfo.msgmnb = ns->msg_ctlmnb; - msginfo.msgssz = MSGSSZ; - msginfo.msgseg = MSGSEG; - down_read(&msg_ids(ns).rwsem); - if (cmd == MSG_INFO) { - msginfo.msgpool = msg_ids(ns).in_use; - msginfo.msgmap = atomic_read(&ns->msg_hdrs); - msginfo.msgtql = atomic_read(&ns->msg_bytes); - } else { - msginfo.msgmap = MSGMAP; - msginfo.msgpool = MSGPOOL; - msginfo.msgtql = MSGTQL; - } - max_id = ipc_get_maxid(&msg_ids(ns)); - up_read(&msg_ids(ns).rwsem); - if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) - return -EFAULT; - return (max_id < 0) ? 0 : max_id; + /* + * We must not return kernel stack data. + * due to padding, it's not enough + * to set all member fields. + */ + err = security_msg_queue_msgctl(NULL, cmd); + if (err) + return err; + + memset(msginfo, 0, sizeof(*msginfo)); + msginfo->msgmni = ns->msg_ctlmni; + msginfo->msgmax = ns->msg_ctlmax; + msginfo->msgmnb = ns->msg_ctlmnb; + msginfo->msgssz = MSGSSZ; + msginfo->msgseg = MSGSEG; + down_read(&msg_ids(ns).rwsem); + if (cmd == MSG_INFO) { + msginfo->msgpool = msg_ids(ns).in_use; + msginfo->msgmap = atomic_read(&ns->msg_hdrs); + msginfo->msgtql = atomic_read(&ns->msg_bytes); + } else { + msginfo->msgmap = MSGMAP; + msginfo->msgpool = MSGPOOL; + msginfo->msgtql = MSGTQL; } + max_id = ipc_get_maxid(&msg_ids(ns)); + up_read(&msg_ids(ns).rwsem); + return (max_id < 0) ? 0 : max_id; +} - case MSG_STAT: - case IPC_STAT: - { - struct msqid64_ds tbuf; - int success_return; - - if (!buf) - return -EFAULT; - - memset(&tbuf, 0, sizeof(tbuf)); +static int msgctl_stat(struct ipc_namespace *ns, int msqid, + int cmd, struct msqid64_ds *p) +{ + int err; + struct msg_queue *msq; + int success_return; - rcu_read_lock(); - if (cmd == MSG_STAT) { - msq = msq_obtain_object(ns, msqid); - if (IS_ERR(msq)) { - err = PTR_ERR(msq); - goto out_unlock; - } - success_return = msq->q_perm.id; - } else { - msq = msq_obtain_object_check(ns, msqid); - if (IS_ERR(msq)) { - err = PTR_ERR(msq); - goto out_unlock; - } - success_return = 0; - } + memset(p, 0, sizeof(*p)); - err = -EACCES; - if (ipcperms(ns, &msq->q_perm, S_IRUGO)) + rcu_read_lock(); + if (cmd == MSG_STAT) { + msq = msq_obtain_object(ns, msqid); + if (IS_ERR(msq)) { + err = PTR_ERR(msq); goto out_unlock; - - err = security_msg_queue_msgctl(msq, cmd); - if (err) + } + success_return = msq->q_perm.id; + } else { + msq = msq_obtain_object_check(ns, msqid); + if (IS_ERR(msq)) { + err = PTR_ERR(msq); goto out_unlock; + } + success_return = 0; + } - kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm); - tbuf.msg_stime = msq->q_stime; - tbuf.msg_rtime = msq->q_rtime; - tbuf.msg_ctime = msq->q_ctime; - tbuf.msg_cbytes = msq->q_cbytes; - tbuf.msg_qnum = msq->q_qnum; - tbuf.msg_qbytes = msq->q_qbytes; - tbuf.msg_lspid = msq->q_lspid; - tbuf.msg_lrpid = msq->q_lrpid; - rcu_read_unlock(); + err = -EACCES; + if (ipcperms(ns, &msq->q_perm, S_IRUGO)) + goto out_unlock; - if (copy_msqid_to_user(buf, &tbuf, version)) - return -EFAULT; - return success_return; - } + err = security_msg_queue_msgctl(msq, cmd); + if (err) + goto out_unlock; + + kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm); + p->msg_stime = msq->q_stime; + p->msg_rtime = msq->q_rtime; + p->msg_ctime = msq->q_ctime; + p->msg_cbytes = msq->q_cbytes; + p->msg_qnum = msq->q_qnum; + p->msg_qbytes = msq->q_qbytes; + p->msg_lspid = msq->q_lspid; + p->msg_lrpid = msq->q_lrpid; + rcu_read_unlock(); - default: - return -EINVAL; - } + return success_return; - return err; out_unlock: rcu_read_unlock(); return err; @@ -556,6 +528,8 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) { int version; struct ipc_namespace *ns; + struct msqid64_ds msqid64; + int err; if (msqid < 0 || cmd < 0) return -EINVAL; @@ -565,13 +539,29 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) switch (cmd) { case IPC_INFO: - case MSG_INFO: + case MSG_INFO: { + struct msginfo msginfo; + err = msgctl_info(ns, msqid, cmd, &msginfo); + if (err < 0) + return err; + if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) + err = -EFAULT; + return err; + } case MSG_STAT: /* msqid is an index rather than a msg queue id */ case IPC_STAT: - return msgctl_nolock(ns, msqid, cmd, version, buf); + err = msgctl_stat(ns, msqid, cmd, &msqid64); + if (err < 0) + return err; + if (copy_msqid_to_user(buf, &msqid64, version)) + err = -EFAULT; + return err; case IPC_SET: + if (copy_msqid_from_user(&msqid64, buf, version)) + return -EFAULT; + /* fallthru */ case IPC_RMID: - return msgctl_down(ns, msqid, cmd, buf, version); + return msgctl_down(ns, msqid, cmd, &msqid64); default: return -EINVAL; } -- cgit v1.2.3-58-ga151 From 4693916846269d633a3664586650dbfac2c5562f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 08:31:16 -0400 Subject: msgctl(): move compat to native Signed-off-by: Al Viro --- ipc/compat.c | 132 ---------------------------------------------------------- ipc/msg.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 132 deletions(-) diff --git a/ipc/compat.c b/ipc/compat.c index fbfd6fb0a68d..c83099a3b265 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -50,22 +50,6 @@ struct compat_semid_ds { 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_ipc_kludge { compat_uptr_t msgp; compat_long_t msgtyp; @@ -391,122 +375,6 @@ COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp, 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; diff --git a/ipc/msg.c b/ipc/msg.c index 322e7bf8b8d1..3400012e1ce8 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -567,6 +567,139 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) } } +#ifdef CONFIG_COMPAT + +struct compat_msqid_ds { + struct compat_ipc_perm msg_perm; + compat_uptr_t msg_first; + compat_uptr_t msg_last; + compat_time_t msg_stime; + compat_time_t msg_rtime; + compat_time_t msg_ctime; + compat_ulong_t msg_lcbytes; + compat_ulong_t msg_lqbytes; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + compat_ipc_pid_t msg_lspid; + compat_ipc_pid_t msg_lrpid; +}; + +static int copy_compat_msqid_from_user(struct msqid64_ds *out, void __user *buf, + int version) +{ + memset(out, 0, sizeof(*out)); + if (version == IPC_64) { + struct compat_msqid64_ds *p = buf; + struct compat_ipc64_perm v; + if (copy_from_user(&v, &p->msg_perm, sizeof(v))) + return -EFAULT; + out->msg_perm.uid = v.uid; + out->msg_perm.gid = v.gid; + out->msg_perm.mode = v.mode; + if (get_user(out->msg_qbytes, &p->msg_qbytes)) + return -EFAULT; + } else { + struct compat_msqid_ds *p = buf; + struct compat_ipc_perm v; + if (copy_from_user(&v, &p->msg_perm, sizeof(v))) + return -EFAULT; + out->msg_perm.uid = v.uid; + out->msg_perm.gid = v.gid; + out->msg_perm.mode = v.mode; + if (get_user(out->msg_qbytes, &p->msg_qbytes)) + return -EFAULT; + } + return 0; +} + +static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, + int version) +{ + if (version == IPC_64) { + struct compat_msqid64_ds v; + memset(&v, 0, sizeof(v)); + v.msg_perm.key = in->msg_perm.key; + v.msg_perm.uid = in->msg_perm.uid; + v.msg_perm.gid = in->msg_perm.gid; + v.msg_perm.cuid = in->msg_perm.cuid; + v.msg_perm.cgid = in->msg_perm.cgid; + v.msg_perm.mode = in->msg_perm.mode; + v.msg_perm.seq = in->msg_perm.seq; + v.msg_stime = in->msg_stime; + v.msg_rtime = in->msg_rtime; + v.msg_ctime = in->msg_ctime; + v.msg_cbytes = in->msg_cbytes; + v.msg_qnum = in->msg_qnum; + v.msg_qbytes = in->msg_qbytes; + v.msg_lspid = in->msg_lspid; + v.msg_lrpid = in->msg_lrpid; + return copy_to_user(buf, &v, sizeof(v)); + } else { + struct compat_msqid_ds v; + memset(&v, 0, sizeof(v)); + v.msg_perm.key = in->msg_perm.key; + SET_UID(v.msg_perm.uid, in->msg_perm.uid); + SET_GID(v.msg_perm.gid, in->msg_perm.gid); + SET_UID(v.msg_perm.cuid, in->msg_perm.cuid); + SET_GID(v.msg_perm.cgid, in->msg_perm.cgid); + v.msg_perm.mode = in->msg_perm.mode; + v.msg_perm.seq = in->msg_perm.seq; + v.msg_stime = in->msg_stime; + v.msg_rtime = in->msg_rtime; + v.msg_ctime = in->msg_ctime; + v.msg_cbytes = in->msg_cbytes; + v.msg_qnum = in->msg_qnum; + v.msg_qbytes = in->msg_qbytes; + v.msg_lspid = in->msg_lspid; + v.msg_lrpid = in->msg_lrpid; + return copy_to_user(buf, &v, sizeof(v)); + } +} + +COMPAT_SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, void __user *, uptr) +{ + struct ipc_namespace *ns; + int err; + struct msqid64_ds msqid64; + int version = compat_ipc_parse_version(&cmd); + + ns = current->nsproxy->ipc_ns; + + if (msqid < 0 || cmd < 0) + return -EINVAL; + + switch (cmd & (~IPC_64)) { + case IPC_INFO: + case MSG_INFO: { + struct msginfo msginfo; + err = msgctl_info(ns, msqid, cmd, &msginfo); + if (err < 0) + return err; + if (copy_to_user(uptr, &msginfo, sizeof(struct msginfo))) + err = -EFAULT; + return err; + } + case IPC_STAT: + case MSG_STAT: + err = msgctl_stat(ns, msqid, cmd, &msqid64); + if (err < 0) + return err; + if (copy_compat_msqid_to_user(uptr, &msqid64, version)) + err = -EFAULT; + return err; + case IPC_SET: + if (copy_compat_msqid_from_user(&msqid64, uptr, version)) + return -EFAULT; + /* fallthru */ + case IPC_RMID: + return msgctl_down(ns, msqid, cmd, &msqid64); + default: + return -EINVAL; + } +} +#endif + static int testmsg(struct msg_msg *msg, long type, int mode) { switch (mode) { -- cgit v1.2.3-58-ga151 From 45a4a64ab485d5c3e76ee79163a24303bf5077fd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 09:11:00 -0400 Subject: semctl(): separate all layout-dependent copyin/copyout Signed-off-by: Al Viro --- ipc/sem.c | 192 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 94 insertions(+), 98 deletions(-) diff --git a/ipc/sem.c b/ipc/sem.c index 9e70cd7a17da..c8029c6bef72 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1177,112 +1177,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; @@ -1531,23 +1514,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,7 +1544,7 @@ 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(); @@ -1591,6 +1568,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 +1580,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,11 +1596,22 @@ 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: - return semctl_setval(ns, semid, semnum, arg); - case IPC_RMID: + 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: - return semctl_down(ns, semid, cmd, version, p); + if (copy_semid_from_user(&semid64, p, version)) + return -EFAULT; + case IPC_RMID: + return semctl_down(ns, semid, cmd, &semid64); default: return -EINVAL; } -- cgit v1.2.3-58-ga151 From c0ebccb6fa1e2c9c3377fa8136e6d8bc006fca64 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 10:03:23 -0400 Subject: semctl(): move compat to native Signed-off-by: Al Viro --- ipc/compat.c | 230 +++++++++-------------------------------------------------- ipc/sem.c | 94 ++++++++++++++++++++++++ ipc/util.h | 6 ++ 3 files changed, 133 insertions(+), 197 deletions(-) diff --git a/ipc/compat.c b/ipc/compat.c index c83099a3b265..3c25ca1e46c7 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -39,214 +39,55 @@ struct compat_msgbuf { char mtext[1]; }; -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_ipc_kludge { compat_uptr_t msgp; compat_long_t msgtyp; }; -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 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) +int get_compat_ipc64_perm(struct ipc64_perm *to, + struct compat_ipc64_perm __user *from) { - if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) + struct compat_ipc64_perm v; + if (copy_from_user(&v, from, sizeof(v))) return -EFAULT; - return __get_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm); + to->uid = v.uid; + to->gid = v.gid; + to->mode = v.mode; + return 0; } -static inline int get_compat_semid_ds(struct semid64_ds *s, - struct compat_semid_ds __user *up) +int get_compat_ipc_perm(struct ipc64_perm *to, + struct compat_ipc_perm __user *from) { - if (!access_ok(VERIFY_READ, up, sizeof(*up))) + struct compat_ipc_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) +void to_compat_ipc64_perm(struct compat_ipc64_perm *to, struct ipc64_perm *from) { - int err; - - if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) - 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->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 inline int put_compat_semid_ds(struct semid64_ds *s, - struct compat_semid_ds __user *up) +void to_compat_ipc_perm(struct compat_ipc_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; -} - -static long do_compat_semctl(int first, int second, int third, u32 pad) -{ - 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; + 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; } static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) @@ -291,7 +132,7 @@ COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, return -EINVAL; if (get_user(pad, (u32 __user *) compat_ptr(ptr))) return -EFAULT; - return do_compat_semctl(first, second, third, pad); + return compat_sys_semctl(first, second, third, pad); case MSGSND: { struct compat_msgbuf __user *up = compat_ptr(ptr); @@ -352,11 +193,6 @@ COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, } #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) { diff --git a/ipc/sem.c b/ipc/sem.c index c8029c6bef72..fcf064d6046a 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1617,6 +1617,100 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) } } +#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_SET: + 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, * and current is THE ONE diff --git a/ipc/util.h b/ipc/util.h index 3a3dfe137bee..c7b7a5ff1f0b 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -204,6 +204,12 @@ struct compat_ipc_perm { 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 -- cgit v1.2.3-58-ga151 From 28327fae62b011216026b66299882c53b95b4500 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 10:10:32 -0400 Subject: ipc: make use of compat ipc_perm helpers Signed-off-by: Al Viro --- ipc/msg.c | 28 ++++------------------------ ipc/shm.c | 30 ++++-------------------------- 2 files changed, 8 insertions(+), 50 deletions(-) diff --git a/ipc/msg.c b/ipc/msg.c index 3400012e1ce8..94690fb53f66 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -591,22 +591,14 @@ static int copy_compat_msqid_from_user(struct msqid64_ds *out, void __user *buf, memset(out, 0, sizeof(*out)); if (version == IPC_64) { struct compat_msqid64_ds *p = buf; - struct compat_ipc64_perm v; - if (copy_from_user(&v, &p->msg_perm, sizeof(v))) + if (get_compat_ipc64_perm(&out->msg_perm, &p->msg_perm)) return -EFAULT; - out->msg_perm.uid = v.uid; - out->msg_perm.gid = v.gid; - out->msg_perm.mode = v.mode; if (get_user(out->msg_qbytes, &p->msg_qbytes)) return -EFAULT; } else { struct compat_msqid_ds *p = buf; - struct compat_ipc_perm v; - if (copy_from_user(&v, &p->msg_perm, sizeof(v))) + if (get_compat_ipc_perm(&out->msg_perm, &p->msg_perm)) return -EFAULT; - out->msg_perm.uid = v.uid; - out->msg_perm.gid = v.gid; - out->msg_perm.mode = v.mode; if (get_user(out->msg_qbytes, &p->msg_qbytes)) return -EFAULT; } @@ -619,13 +611,7 @@ static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, if (version == IPC_64) { struct compat_msqid64_ds v; memset(&v, 0, sizeof(v)); - v.msg_perm.key = in->msg_perm.key; - v.msg_perm.uid = in->msg_perm.uid; - v.msg_perm.gid = in->msg_perm.gid; - v.msg_perm.cuid = in->msg_perm.cuid; - v.msg_perm.cgid = in->msg_perm.cgid; - v.msg_perm.mode = in->msg_perm.mode; - v.msg_perm.seq = in->msg_perm.seq; + to_compat_ipc64_perm(&v.msg_perm, &in->msg_perm); v.msg_stime = in->msg_stime; v.msg_rtime = in->msg_rtime; v.msg_ctime = in->msg_ctime; @@ -638,13 +624,7 @@ static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, } else { struct compat_msqid_ds v; memset(&v, 0, sizeof(v)); - v.msg_perm.key = in->msg_perm.key; - SET_UID(v.msg_perm.uid, in->msg_perm.uid); - SET_GID(v.msg_perm.gid, in->msg_perm.gid); - SET_UID(v.msg_perm.cuid, in->msg_perm.cuid); - SET_GID(v.msg_perm.cgid, in->msg_perm.cgid); - v.msg_perm.mode = in->msg_perm.mode; - v.msg_perm.seq = in->msg_perm.seq; + to_compat_ipc_perm(&v.msg_perm, &in->msg_perm); v.msg_stime = in->msg_stime; v.msg_rtime = in->msg_rtime; v.msg_ctime = in->msg_ctime; diff --git a/ipc/shm.c b/ipc/shm.c index 87334ee3acb3..2e31545035a6 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1161,13 +1161,7 @@ static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in, 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; + 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; @@ -1179,13 +1173,8 @@ static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in, } 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; - 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; @@ -1203,22 +1192,11 @@ static int copy_compat_shmid_from_user(struct shmid64_ds *out, void __user *buf, 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; + return get_compat_ipc64_perm(&out->shm_perm, &p->shm_perm); } 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 get_compat_ipc_perm(&out->shm_perm, &p->shm_perm); } - return 0; } COMPAT_SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, void __user *, uptr) -- cgit v1.2.3-58-ga151 From 20bc2a3aff5a88a666e81182fd277ea2a521fd3d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 10:27:22 -0400 Subject: ipc(2): move compat to native Signed-off-by: Al Viro --- ipc/compat.c | 98 +++-------------------------------------------------------- ipc/syscall.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 95 deletions(-) diff --git a/ipc/compat.c b/ipc/compat.c index 3c25ca1e46c7..00c2e3beccc8 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -39,11 +39,6 @@ struct compat_msgbuf { char mtext[1]; }; -struct compat_ipc_kludge { - compat_uptr_t msgp; - compat_long_t msgtyp; -}; - int get_compat_ipc64_perm(struct ipc64_perm *to, struct compat_ipc64_perm __user *from) { @@ -104,95 +99,6 @@ static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bu 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 compat_sys_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(msgsnd, int, msqid, compat_uptr_t, msgp, compat_ssize_t, msgsz, int, msgflg) { @@ -211,6 +117,10 @@ COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp, msgflg, compat_do_msg_fill); } +#ifndef COMPAT_SHMLBA +#define COMPAT_SHMLBA SHMLBA +#endif + COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg) { unsigned long ret; diff --git a/ipc/syscall.c b/ipc/syscall.c index 52429489cde0..667022746ca5 100644 --- a/ipc/syscall.c +++ b/ipc/syscall.c @@ -5,12 +5,12 @@ * the individual syscalls instead. */ #include +#include #ifdef __ARCH_WANT_SYS_IPC #include #include #include -#include #include SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, @@ -97,3 +97,91 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, } } #endif + +#ifdef CONFIG_COMPAT +#include + +#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 -- cgit v1.2.3-58-ga151 From 9b1404c24a357332cb2a6df7c4337e943a4545fd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 10:34:35 -0400 Subject: msgrcv(2), msgsnd(2): move compat to native Signed-off-by: Al Viro --- include/linux/msg.h | 8 -------- ipc/compat.c | 37 ------------------------------------- ipc/msg.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 47 deletions(-) diff --git a/include/linux/msg.h b/include/linux/msg.h index f3f302f9c197..4e5ec3cbf464 100644 --- a/include/linux/msg.h +++ b/include/linux/msg.h @@ -31,12 +31,4 @@ struct msg_queue { struct list_head q_senders; }; -/* Helper routines for sys_msgsnd and sys_msgrcv */ -extern long do_msgsnd(int msqid, long mtype, void __user *mtext, - size_t msgsz, int msgflg); -extern long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, - int msgflg, - long (*msg_fill)(void __user *, struct msg_msg *, - size_t)); - #endif /* _LINUX_MSG_H */ diff --git a/ipc/compat.c b/ipc/compat.c index 00c2e3beccc8..0586687c3e31 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -34,11 +34,6 @@ #include "util.h" -struct compat_msgbuf { - compat_long_t mtype; - char mtext[1]; -}; - int get_compat_ipc64_perm(struct ipc64_perm *to, struct compat_ipc64_perm __user *from) { @@ -85,38 +80,6 @@ void to_compat_ipc_perm(struct compat_ipc_perm *to, struct ipc64_perm *from) to->seq = from->seq; } -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_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); -} - #ifndef COMPAT_SHMLBA #define COMPAT_SHMLBA SHMLBA #endif diff --git a/ipc/msg.c b/ipc/msg.c index 94690fb53f66..855da19c765a 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -730,7 +730,7 @@ static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg, return 0; } -long do_msgsnd(int msqid, long mtype, void __user *mtext, +static long do_msgsnd(int msqid, long mtype, void __user *mtext, size_t msgsz, int msgflg) { struct msg_queue *msq; @@ -853,6 +853,25 @@ SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); } +#ifdef CONFIG_COMPAT + +struct compat_msgbuf { + compat_long_t mtype; + char mtext[1]; +}; + +COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp, + compat_ssize_t, msgsz, int, msgflg) +{ + struct compat_msgbuf __user *up = compat_ptr(msgp); + compat_long_t mtype; + + if (get_user(mtype, &up->mtype)) + return -EFAULT; + return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg); +} +#endif + static inline int convert_mode(long *msgtyp, int msgflg) { if (msgflg & MSG_COPY) @@ -949,7 +968,7 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode) return found ?: ERR_PTR(-EAGAIN); } -long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, +static long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, long (*msg_handler)(void __user *, struct msg_msg *, size_t)) { int mode; @@ -1113,6 +1132,28 @@ SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill); } +#ifdef CONFIG_COMPAT +static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) +{ + struct compat_msgbuf __user *msgp = dest; + size_t msgsz; + + if (put_user(msg->m_type, &msgp->mtype)) + return -EFAULT; + + msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; + if (store_msg(msgp->mtext, msg, msgsz)) + return -EFAULT; + return msgsz; +} + +COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp, + compat_ssize_t, msgsz, compat_long_t, msgtyp, int, msgflg) +{ + return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp, + msgflg, compat_do_msg_fill); +} +#endif void msg_init_ns(struct ipc_namespace *ns) { -- cgit v1.2.3-58-ga151 From a78ee9ed2f828e1960f366bf7ab204e7f19924c7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 10:38:28 -0400 Subject: shmat(2): move compat to native Signed-off-by: Al Viro --- ipc/compat.c | 16 ---------------- ipc/shm.c | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/ipc/compat.c b/ipc/compat.c index 0586687c3e31..871d07da0a52 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -80,22 +80,6 @@ void to_compat_ipc_perm(struct compat_ipc_perm *to, struct ipc64_perm *from) to->seq = from->seq; } -#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; -} - 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 2e31545035a6..342024de3b9d 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1439,6 +1439,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. -- cgit v1.2.3-58-ga151 From 44ee454670122a959112caaa7aad86d8cacab1ff Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 10:50:14 -0400 Subject: semtimedop(): move compat to native ... and finally kill the sodding compat_convert_timespec() Signed-off-by: Al Viro --- include/linux/compat.h | 9 --------- ipc/compat.c | 10 ---------- ipc/sem.c | 44 +++++++++++++++++++++++++++++++++----------- kernel/compat.c | 23 ----------------------- 4 files changed, 33 insertions(+), 53 deletions(-) diff --git a/include/linux/compat.h b/include/linux/compat.h index 5a6a109b4a50..edae425ca8c0 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -171,15 +171,6 @@ extern int get_compat_itimerspec64(struct itimerspec64 *its, extern int put_compat_itimerspec64(const struct itimerspec64 *its, struct compat_itimerspec __user *uits); -/* - * This function convert a timespec if necessary and returns a *user - * space* pointer. If no conversion is necessary, it returns the - * initial pointer. NULL is a legitimate argument and will always - * output NULL. - */ -extern int compat_convert_timespec(struct timespec __user **, - const void __user *); - struct compat_iovec { compat_uptr_t iov_base; compat_size_t iov_len; diff --git a/ipc/compat.c b/ipc/compat.c index 871d07da0a52..b17bf93d7b49 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -79,13 +79,3 @@ void to_compat_ipc_perm(struct compat_ipc_perm *to, struct ipc64_perm *from) to->mode = from->mode; to->seq = from->seq; } - -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); -} diff --git a/ipc/sem.c b/ipc/sem.c index fcf064d6046a..6b832b7fa9fc 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1855,8 +1855,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 timespec *timeout) { int error = -EINVAL; struct sem_array *sma; @@ -1887,17 +1887,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 = timespec_to_jiffies(timeout); } max = 0; @@ -2112,10 +2107,37 @@ out_free: return error; } +SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, + unsigned, nsops, const struct timespec __user *, timeout) +{ + if (timeout) { + struct timespec ts; + if (copy_from_user(&ts, timeout, sizeof(*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 timespec ts; + if (compat_get_timespec(&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 diff --git a/kernel/compat.c b/kernel/compat.c index 6f0a0e723a06..772e038d04d9 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -200,29 +200,6 @@ int compat_put_timespec(const struct timespec *ts, void __user *uts) } EXPORT_SYMBOL_GPL(compat_put_timespec); -int compat_convert_timespec(struct timespec __user **kts, - const void __user *cts) -{ - struct timespec ts; - struct timespec __user *uts; - - if (!cts || COMPAT_USE_64BIT_TIME) { - *kts = (struct timespec __user *)cts; - return 0; - } - - uts = compat_alloc_user_space(sizeof(ts)); - if (!uts) - return -EFAULT; - if (compat_get_timespec(&ts, cts)) - return -EFAULT; - if (copy_to_user(uts, &ts, sizeof(ts))) - return -EFAULT; - - *kts = uts; - return 0; -} - int get_compat_itimerval(struct itimerval *o, const struct compat_itimerval __user *i) { struct compat_itimerval v32; -- cgit v1.2.3-58-ga151 From 8ac72a462371b39579790394efcea014388f737d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 9 Jul 2017 11:42:32 -0400 Subject: get rid of SYSVIPC_COMPAT on ia64 it's always false, since it depends on COMPAT and ia64 is not biarch. Signed-off-by: Al Viro --- arch/ia64/Kconfig.debug | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/ia64/Kconfig.debug b/arch/ia64/Kconfig.debug index de9d507ba0fd..4763887ba368 100644 --- a/arch/ia64/Kconfig.debug +++ b/arch/ia64/Kconfig.debug @@ -56,9 +56,4 @@ config IA64_DEBUG_IRQ and restore instructions. It's useful for tracking down spinlock problems, but slow! If you're unsure, select N. -config SYSVIPC_COMPAT - bool - depends on COMPAT && SYSVIPC - default y - endmenu -- cgit v1.2.3-58-ga151 From 3ef56dc2678258b7211b1870d034c1666becb2bd Mon Sep 17 00:00:00 2001 From: Deepa Dinamani Date: Wed, 2 Aug 2017 19:51:10 -0700 Subject: ipc: Make sys_semtimedop() y2038 safe struct timespec is not y2038 safe on 32 bit machines. Replace timespec with y2038 safe struct timespec64. Note that the patch only changes the internals without modifying the syscall interface. This will be part of a separate series. Signed-off-by: Deepa Dinamani Reviewed-by: Arnd Bergmann Signed-off-by: Al Viro --- ipc/sem.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ipc/sem.c b/ipc/sem.c index 6b832b7fa9fc..feea26f897e7 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1856,7 +1856,7 @@ out: } static long do_semtimedop(int semid, struct sembuf __user *tsops, - unsigned nsops, const struct timespec *timeout) + unsigned nsops, const struct timespec64 *timeout) { int error = -EINVAL; struct sem_array *sma; @@ -1892,7 +1892,7 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops, error = -EINVAL; goto out_free; } - jiffies_left = timespec_to_jiffies(timeout); + jiffies_left = timespec64_to_jiffies(timeout); } max = 0; @@ -2111,8 +2111,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, unsigned, nsops, const struct timespec __user *, timeout) { if (timeout) { - struct timespec ts; - if (copy_from_user(&ts, timeout, sizeof(*timeout))) + struct timespec64 ts; + if (get_timespec64(&ts, timeout)) return -EFAULT; return do_semtimedop(semid, tsops, nsops, &ts); } @@ -2125,8 +2125,8 @@ COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems, const struct compat_timespec __user *, timeout) { if (timeout) { - struct timespec ts; - if (compat_get_timespec(&ts, timeout)) + struct timespec64 ts; + if (compat_get_timespec64(&ts, timeout)) return -EFAULT; return do_semtimedop(semid, tsems, nsops, &ts); } -- cgit v1.2.3-58-ga151 From b9047726386bb538cf5e4f52a9f04eb556eebc67 Mon Sep 17 00:00:00 2001 From: Deepa Dinamani Date: Wed, 2 Aug 2017 19:51:11 -0700 Subject: ipc: mqueue: Replace timespec with timespec64 struct timespec is not y2038 safe. Replace all uses of timespec by y2038 safe struct timespec64. Even though timespec is used here to represent timeouts, replace these with timespec64 so that it facilitates in verification by creating a y2038 safe kernel image that is free of timespec. The syscall interfaces themselves are not changed as part of the patch. They will be part of a different series. Signed-off-by: Deepa Dinamani Cc: Paul Moore Cc: Richard Guy Briggs Reviewed-by: Richard Guy Briggs Reviewed-by: Arnd Bergmann Acked-by: Paul Moore Signed-off-by: Al Viro --- include/linux/audit.h | 6 +++--- ipc/mqueue.c | 28 ++++++++++++++-------------- kernel/audit.h | 2 +- kernel/auditsc.c | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 2150bdccfbab..74d4d4e8e3db 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -351,7 +351,7 @@ extern int __audit_socketcall(int nargs, unsigned long *args); extern int __audit_sockaddr(int len, void *addr); extern void __audit_fd_pair(int fd1, int fd2); extern void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr); -extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout); +extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec64 *abs_timeout); extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification); extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, @@ -412,7 +412,7 @@ static inline void audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr) if (unlikely(!audit_dummy_context())) __audit_mq_open(oflag, mode, attr); } -static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout) +static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec64 *abs_timeout) { if (unlikely(!audit_dummy_context())) __audit_mq_sendrecv(mqdes, msg_len, msg_prio, abs_timeout); @@ -549,7 +549,7 @@ static inline void audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr) { } static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, - const struct timespec *abs_timeout) + const struct timespec64 *abs_timeout) { } static inline void audit_mq_notify(mqd_t mqdes, const struct sigevent *notification) 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/kernel/audit.h b/kernel/audit.h index b331d9b83f63..9b110ae17ee3 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -182,7 +182,7 @@ struct audit_context { mqd_t mqdes; size_t msg_len; unsigned int msg_prio; - struct timespec abs_timeout; + struct timespec64 abs_timeout; } mq_sendrecv; struct { int oflag; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 3260ba2312a9..daee2d5bd03a 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1235,11 +1235,11 @@ static void show_special(struct audit_context *context, int *call_panic) case AUDIT_MQ_SENDRECV: audit_log_format(ab, "mqdes=%d msg_len=%zd msg_prio=%u " - "abs_timeout_sec=%ld abs_timeout_nsec=%ld", + "abs_timeout_sec=%lld abs_timeout_nsec=%ld", context->mq_sendrecv.mqdes, context->mq_sendrecv.msg_len, context->mq_sendrecv.msg_prio, - context->mq_sendrecv.abs_timeout.tv_sec, + (long long) context->mq_sendrecv.abs_timeout.tv_sec, context->mq_sendrecv.abs_timeout.tv_nsec); break; case AUDIT_MQ_NOTIFY: @@ -2083,15 +2083,15 @@ void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr) * */ void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, - const struct timespec *abs_timeout) + const struct timespec64 *abs_timeout) { struct audit_context *context = current->audit_context; - struct timespec *p = &context->mq_sendrecv.abs_timeout; + struct timespec64 *p = &context->mq_sendrecv.abs_timeout; if (abs_timeout) - memcpy(p, abs_timeout, sizeof(struct timespec)); + memcpy(p, abs_timeout, sizeof(*p)); else - memset(p, 0, sizeof(struct timespec)); + memset(p, 0, sizeof(*p)); context->mq_sendrecv.mqdes = mqdes; context->mq_sendrecv.msg_len = msg_len; -- cgit v1.2.3-58-ga151 From 50578ea97a2a352c109bd1657e667b212faf2cbb Mon Sep 17 00:00:00 2001 From: Deepa Dinamani Date: Wed, 2 Aug 2017 19:51:12 -0700 Subject: ipc: msg: Make msg_queue timestamps y2038 safe time_t is not y2038 safe. Replace all uses of time_t by y2038 safe time64_t. Similarly, replace the calls to get_seconds() with y2038 safe ktime_get_real_seconds(). Note that this preserves fast access on 64 bit systems, but 32 bit systems need sequence counters. The syscall interfaces themselves are not changed as part of the patch. They will be part of a different series. Signed-off-by: Deepa Dinamani Reviewed-by: Arnd Bergmann Signed-off-by: Al Viro --- include/linux/msg.h | 7 ++++--- ipc/msg.c | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/linux/msg.h b/include/linux/msg.h index 4e5ec3cbf464..05115342daa3 100644 --- a/include/linux/msg.h +++ b/include/linux/msg.h @@ -2,6 +2,7 @@ #define _LINUX_MSG_H #include +#include #include /* one msg_msg structure for each message */ @@ -17,9 +18,9 @@ struct msg_msg { /* one msq_queue structure for each present queue on the system */ struct msg_queue { struct kern_ipc_perm q_perm; - time_t q_stime; /* last msgsnd time */ - time_t q_rtime; /* last msgrcv time */ - time_t q_ctime; /* last change time */ + time64_t q_stime; /* last msgsnd time */ + time64_t q_rtime; /* last msgrcv time */ + time64_t q_ctime; /* last change time */ unsigned long q_cbytes; /* current number of bytes on queue */ unsigned long q_qnum; /* number of messages in queue */ unsigned long q_qbytes; /* max number of bytes on queue */ diff --git a/ipc/msg.c b/ipc/msg.c index 855da19c765a..0e7ccfc0700b 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -133,7 +133,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) } msq->q_stime = msq->q_rtime = 0; - msq->q_ctime = get_seconds(); + msq->q_ctime = ktime_get_real_seconds(); msq->q_cbytes = msq->q_qnum = 0; msq->q_qbytes = ns->msg_ctlmnb; msq->q_lspid = msq->q_lrpid = 0; @@ -406,7 +406,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, msq->q_qbytes = msqid64->msg_qbytes; - msq->q_ctime = get_seconds(); + msq->q_ctime = ktime_get_real_seconds(); /* * Sleeping receivers might be excluded by * stricter permissions. @@ -1181,7 +1181,7 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it) struct msg_queue *msq = it; seq_printf(s, - "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n", + "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10llu %10llu %10llu\n", msq->q_perm.key, msq->q_perm.id, msq->q_perm.mode, -- cgit v1.2.3-58-ga151 From e54d02b23c5eed3aa0ffe54e659dfe1c9084c262 Mon Sep 17 00:00:00 2001 From: Deepa Dinamani Date: Wed, 2 Aug 2017 19:51:13 -0700 Subject: ipc: sem: Make sem_array timestamps y2038 safe time_t is not y2038 safe. Replace all uses of time_t by y2038 safe time64_t. Similarly, replace the calls to get_seconds() with y2038 safe ktime_get_real_seconds(). Note that this preserves fast access on 64 bit systems, but 32 bit systems need sequence counters. The syscall interface themselves are not changed as part of the patch. They will be part of a different series. Signed-off-by: Deepa Dinamani Reviewed-by: Arnd Bergmann Signed-off-by: Al Viro --- include/linux/sem.h | 3 ++- ipc/sem.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/linux/sem.h b/include/linux/sem.h index be5cf2ea14ad..867f86f1a20b 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -4,6 +4,7 @@ #include #include #include +#include #include struct task_struct; @@ -30,7 +31,7 @@ struct sem { /* One sem_array data structure for each set of semaphores in the system. */ struct sem_array { struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */ - time_t sem_ctime; /* create/last semctl() time */ + time64_t sem_ctime; /* create/last semctl() time */ struct list_head pending_alter; /* pending operations */ /* that alter the array */ struct list_head pending_const; /* pending complex operations */ diff --git a/ipc/sem.c b/ipc/sem.c index feea26f897e7..f25ea86774c0 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -511,7 +511,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 +1162,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; @@ -1309,7 +1309,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); @@ -1437,7 +1437,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; @@ -1547,7 +1547,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, 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; @@ -2292,7 +2292,7 @@ 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; + time64_t sem_otime; /* * The proc interface isn't aware of sem_lock(), it calls @@ -2305,7 +2305,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, -- cgit v1.2.3-58-ga151 From 7ff2819e8dd5b528887dfbe4ff395f5d2142edff Mon Sep 17 00:00:00 2001 From: Deepa Dinamani Date: Wed, 2 Aug 2017 19:51:14 -0700 Subject: ipc: shm: Make shmid_kernel timestamps y2038 safe time_t is not y2038 safe. Replace all uses of time_t by y2038 safe time64_t. Similarly, replace the calls to get_seconds() with y2038 safe ktime_get_real_seconds(). Note that this preserves fast access on 64 bit systems, but 32 bit systems need sequence counters. The syscall interfaces themselves are not changed as part of the patch. They will be part of a different series. Signed-off-by: Deepa Dinamani Reviewed-by: Arnd Bergmann Signed-off-by: Al Viro --- include/linux/shm.h | 6 +++--- ipc/shm.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/linux/shm.h b/include/linux/shm.h index 04e881829625..7bb897b25309 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -12,9 +12,9 @@ struct shmid_kernel /* private to the kernel */ struct file *shm_file; unsigned long shm_nattch; unsigned long shm_segsz; - time_t shm_atim; - time_t shm_dtim; - time_t shm_ctim; + time64_t shm_atim; + time64_t shm_dtim; + time64_t shm_ctim; pid_t shm_cprid; pid_t shm_lprid; struct user_struct *mlock_user; diff --git a/ipc/shm.c b/ipc/shm.c index 342024de3b9d..f3d6408d6de1 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -200,7 +200,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 +287,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 +592,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; @@ -846,7 +846,7 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, 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; @@ -1586,7 +1586,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, -- cgit v1.2.3-58-ga151 From aaed2dd8a31359e5767ee099ecbb078d55be4d29 Mon Sep 17 00:00:00 2001 From: Deepa Dinamani Date: Wed, 2 Aug 2017 19:51:15 -0700 Subject: utimes: Make utimes y2038 safe struct timespec is not y2038 safe on 32 bit machines. Replace timespec with y2038 safe struct timespec64. Note that the patch only changes the internals without modifying the syscall interfaces. This will be part of a separate series. Signed-off-by: Deepa Dinamani Reviewed-by: Arnd Bergmann Signed-off-by: Al Viro --- fs/utimes.c | 23 ++++++++++++----------- include/linux/time.h | 2 +- init/initramfs.c | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/fs/utimes.c b/fs/utimes.c index 6571d8c848a0..51edb9f9507c 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -22,7 +22,7 @@ */ SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times) { - struct timespec tv[2]; + struct timespec64 tv[2]; if (times) { if (get_user(tv[0].tv_sec, ×->actime) || @@ -44,7 +44,7 @@ static bool nsec_valid(long nsec) return nsec >= 0 && nsec <= 999999999; } -static int utimes_common(const struct path *path, struct timespec *times) +static int utimes_common(const struct path *path, struct timespec64 *times) { int error; struct iattr newattrs; @@ -115,7 +115,7 @@ out: * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ -long do_utimes(int dfd, const char __user *filename, struct timespec *times, +long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, int flags) { int error = -EINVAL; @@ -167,10 +167,11 @@ out: SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, struct timespec __user *, utimes, int, flags) { - struct timespec tstimes[2]; + struct timespec64 tstimes[2]; if (utimes) { - if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) + if ((get_timespec64(&tstimes[0], &utimes[0]) || + get_timespec64(&tstimes[1], &utimes[1]))) return -EFAULT; /* Nothing to do, we must not even check the path. */ @@ -186,7 +187,7 @@ SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, struct timeval __user *, utimes) { struct timeval times[2]; - struct timespec tstimes[2]; + struct timespec64 tstimes[2]; if (utimes) { if (copy_from_user(×, utimes, sizeof(times))) @@ -224,7 +225,7 @@ SYSCALL_DEFINE2(utimes, char __user *, filename, COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename, struct compat_utimbuf __user *, t) { - struct timespec tv[2]; + struct timespec64 tv[2]; if (t) { if (get_user(tv[0].tv_sec, &t->actime) || @@ -238,11 +239,11 @@ COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename, COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, struct compat_timespec __user *, t, int, flags) { - struct timespec tv[2]; + struct timespec64 tv[2]; if (t) { - if (compat_get_timespec(&tv[0], &t[0]) || - compat_get_timespec(&tv[1], &t[1])) + if (compat_get_timespec64(&tv[0], &t[0]) || + compat_get_timespec64(&tv[1], &t[1])) return -EFAULT; if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT) @@ -253,7 +254,7 @@ COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filena COMPAT_SYSCALL_DEFINE3(futimesat, unsigned int, dfd, const char __user *, filename, struct compat_timeval __user *, t) { - struct timespec tv[2]; + struct timespec64 tv[2]; if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || diff --git a/include/linux/time.h b/include/linux/time.h index 4abb32d4c6b8..3d0cd017f0d7 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -178,7 +178,7 @@ extern int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue); extern int do_getitimer(int which, struct itimerval *value); -extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags); +extern long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, int flags); /* * Similar to the struct tm in userspace , but it needs to be here so diff --git a/init/initramfs.c b/init/initramfs.c index 8a532050043f..e64bf7b4c1ca 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -110,7 +110,7 @@ static void __init free_hash(void) static long __init do_utime(char *filename, time_t mtime) { - struct timespec t[2]; + struct timespec64 t[2]; t[0].tv_sec = mtime; t[0].tv_nsec = 0; -- cgit v1.2.3-58-ga151