Loading ipc/compat.c +33 −197 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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) { Loading ipc/sem.c +94 −0 Original line number Diff line number Diff line Loading @@ -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 Loading ipc/util.h +6 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
ipc/compat.c +33 −197 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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) { Loading
ipc/sem.c +94 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
ipc/util.h +6 −0 Original line number Diff line number Diff line Loading @@ -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 Loading