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(-) (limited to 'ipc/sem.c') 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 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/sem.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'ipc/sem.c') 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 -- cgit v1.2.3 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 --- ipc/sem.c | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) (limited to 'ipc/sem.c') 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 -- cgit v1.2.3 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(-) (limited to 'ipc/sem.c') 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 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 --- ipc/sem.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'ipc/sem.c') 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