mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 12:43:29 +02:00
Merge branch 'work.ipc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull ipc compat cleanup and 64-bit time_t from Al Viro: "IPC copyin/copyout sanitizing, including 64bit time_t work from Deepa Dinamani" * 'work.ipc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: utimes: Make utimes y2038 safe ipc: shm: Make shmid_kernel timestamps y2038 safe ipc: sem: Make sem_array timestamps y2038 safe ipc: msg: Make msg_queue timestamps y2038 safe ipc: mqueue: Replace timespec with timespec64 ipc: Make sys_semtimedop() y2038 safe get rid of SYSVIPC_COMPAT on ia64 semtimedop(): move compat to native shmat(2): move compat to native msgrcv(2), msgsnd(2): move compat to native ipc(2): move compat to native ipc: make use of compat ipc_perm helpers semctl(): move compat to native semctl(): separate all layout-dependent copyin/copyout msgctl(): move compat to native msgctl(): split the actual work from copyin/copyout ipc: move compat shmctl to native shmctl: split the work from copyin/copyout
This commit is contained in:
354
ipc/sem.c
354
ipc/sem.c
@@ -512,7 +512,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) {
|
||||
@@ -1163,14 +1163,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;
|
||||
@@ -1178,112 +1178,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;
|
||||
memset(semid64, 0, sizeof(*semid64));
|
||||
|
||||
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;
|
||||
}
|
||||
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))
|
||||
rcu_read_lock();
|
||||
if (cmd == SEM_STAT) {
|
||||
sma = sem_obtain_object(ns, semid);
|
||||
if (IS_ERR(sma)) {
|
||||
err = PTR_ERR(sma);
|
||||
goto out_unlock;
|
||||
|
||||
err = security_sem_semctl(sma, cmd);
|
||||
if (err)
|
||||
}
|
||||
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 = 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;
|
||||
|
||||
@@ -1327,7 +1310,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);
|
||||
@@ -1455,7 +1438,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;
|
||||
@@ -1532,23 +1515,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;
|
||||
@@ -1568,10 +1545,10 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
|
||||
goto out_up;
|
||||
case IPC_SET:
|
||||
sem_lock(sma, NULL, -1);
|
||||
err = ipc_update_perm(&semid64.sem_perm, ipcp);
|
||||
err = ipc_update_perm(&semid64->sem_perm, ipcp);
|
||||
if (err)
|
||||
goto out_unlock0;
|
||||
sma->sem_ctime = get_seconds();
|
||||
sma->sem_ctime = ktime_get_real_seconds();
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
@@ -1592,6 +1569,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;
|
||||
@@ -1602,9 +1581,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:
|
||||
@@ -1612,16 +1597,121 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
@@ -1766,8 +1856,8 @@ out:
|
||||
return un;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
|
||||
unsigned, nsops, const struct timespec __user *, timeout)
|
||||
static long do_semtimedop(int semid, struct sembuf __user *tsops,
|
||||
unsigned nsops, const struct timespec64 *timeout)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
struct sem_array *sma;
|
||||
@@ -1798,17 +1888,12 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
struct timespec _timeout;
|
||||
if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) {
|
||||
error = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
|
||||
_timeout.tv_nsec >= 1000000000L) {
|
||||
if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 ||
|
||||
timeout->tv_nsec >= 1000000000L) {
|
||||
error = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
jiffies_left = timespec_to_jiffies(&_timeout);
|
||||
jiffies_left = timespec64_to_jiffies(timeout);
|
||||
}
|
||||
|
||||
max = 0;
|
||||
@@ -2023,10 +2108,37 @@ out_free:
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
|
||||
unsigned, nsops, const struct timespec __user *, timeout)
|
||||
{
|
||||
if (timeout) {
|
||||
struct timespec64 ts;
|
||||
if (get_timespec64(&ts, timeout))
|
||||
return -EFAULT;
|
||||
return do_semtimedop(semid, tsops, nsops, &ts);
|
||||
}
|
||||
return do_semtimedop(semid, tsops, nsops, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems,
|
||||
unsigned, nsops,
|
||||
const struct compat_timespec __user *, timeout)
|
||||
{
|
||||
if (timeout) {
|
||||
struct timespec64 ts;
|
||||
if (compat_get_timespec64(&ts, timeout))
|
||||
return -EFAULT;
|
||||
return do_semtimedop(semid, tsems, nsops, &ts);
|
||||
}
|
||||
return do_semtimedop(semid, tsems, nsops, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,
|
||||
unsigned, nsops)
|
||||
{
|
||||
return sys_semtimedop(semid, tsops, nsops, NULL);
|
||||
return do_semtimedop(semid, tsops, nsops, NULL);
|
||||
}
|
||||
|
||||
/* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between
|
||||
@@ -2183,7 +2295,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
|
||||
struct user_namespace *user_ns = seq_user_ns(s);
|
||||
struct kern_ipc_perm *ipcp = it;
|
||||
struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
|
||||
time_t sem_otime;
|
||||
time64_t sem_otime;
|
||||
|
||||
/*
|
||||
* The proc interface isn't aware of sem_lock(), it calls
|
||||
@@ -2196,7 +2308,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,
|
||||
|
Reference in New Issue
Block a user