summaryrefslogtreecommitdiff
path: root/ipc/sem.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/sem.c')
-rw-r--r--ipc/sem.c94
1 files changed, 94 insertions, 0 deletions
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