From: Arnd Bergmann - the actual handlers are always called with IPC_64 structures - i386 user space using IPC_OLD should now also work on ia64 and x86_64 - shmctl(..., IPC_INFO, ...) now always needs to be handled - the mips hack is gone --- ipc/compat.c | 192 ++++++++++++++++++++++++++++++++++------------------------- 1 files changed, 113 insertions(+), 79 deletions(-) diff -puN ipc/compat.c~compat-ipc-syscalls-fixes ipc/compat.c --- 25/ipc/compat.c~compat-ipc-syscalls-fixes 2004-02-20 23:37:08.000000000 -0800 +++ 25-akpm/ipc/compat.c 2004-02-20 23:37:08.000000000 -0800 @@ -96,6 +96,18 @@ struct compat_ipc_kludge { 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; @@ -108,16 +120,19 @@ extern int sem_ctls[]; static inline int compat_ipc_parse_version(int *cmd) { - if (*cmd & IPC_64) { -#if defined(CONFIG_IA64) || defined(CONFIG_X86_64) - /* why is this needed? */ - *cmd ^= IPC_64; + 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 IPC_64; - } else { - return IPC_OLD; - } + return version; } + static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64, struct compat_ipc64_perm *up64) { @@ -129,7 +144,7 @@ static inline int __get_compat_ipc64_per return err; } -static inline int __get_compat_ipc_perm(struct ipc_perm *p, +static inline int __get_compat_ipc_perm(struct ipc64_perm *p, struct compat_ipc_perm *up) { int err; @@ -155,7 +170,7 @@ static inline int __put_compat_ipc64_per return err; } -static inline int __put_compat_ipc_perm(struct ipc_perm *p, +static inline int __put_compat_ipc_perm(struct ipc64_perm *p, struct compat_ipc_perm *up) { int err; @@ -184,7 +199,7 @@ static inline int get_compat_semid64_ds( return __get_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); } -static inline int get_compat_semid_ds(struct semid_ds *s, +static inline int get_compat_semid_ds(struct semid64_ds *s, struct compat_semid_ds *up) { if (!access_ok (VERIFY_READ, up, sizeof(*up))) @@ -206,7 +221,7 @@ static inline int put_compat_semid64_ds( return err; } -static inline int put_compat_semid_ds(struct semid_ds *s, +static inline int put_compat_semid_ds(struct semid64_ds *s, struct compat_semid_ds *up) { int err; @@ -238,7 +253,6 @@ long compat_sys_semctl(int first, int se u32 pad; int err, err2; struct semid64_ds s64; - struct semid_ds s; int version = compat_ipc_parse_version(&third); if (!uptr) @@ -265,18 +279,15 @@ long compat_sys_semctl(int first, int se case IPC_STAT: case SEM_STAT: + fourth.__pad = &s64; + err = do_semctl(first, second, third, fourth); + if (err < 0) + break; + if (version == IPC_64) { - fourth.__pad = &s64; - err = do_semctl(first, second, third, fourth); - if (err < 0) - break; err2 = put_compat_semid64_ds(&s64, compat_ptr(pad)); } else { - fourth.__pad = &s; - err = do_semctl(first, second, third, fourth); - if (err < 0) - break; - err2 = put_compat_semid_ds(&s, compat_ptr(pad)); + err2 = put_compat_semid_ds(&s64, compat_ptr(pad)); } if (err2) err = -EFAULT; @@ -285,15 +296,13 @@ long compat_sys_semctl(int first, int se case IPC_SET: if (version == IPC_64) { err = get_compat_semid64_ds(&s64, compat_ptr(pad)); - if (err) - break; - fourth.__pad = &s64; } else { - err = get_compat_semid_ds(&s, compat_ptr(pad)); - if (err) - break; - fourth.__pad = &s; + err = get_compat_semid_ds(&s64, compat_ptr(pad)); } + if (err) + break; + + fourth.__pad = &s64; err = do_semctl(first, second, third, fourth); break; @@ -316,11 +325,7 @@ long compat_sys_msgsnd(int first, int se if (second < 0 || (second >= MAXBUF - sizeof(struct msgbuf))) return -EINVAL; -#ifdef CONFIG_MIPS64 /* FIXME: This looks wrong */ - p = kmalloc(second + sizeof(struct msgbuf) + 4, GFP_USER); -#else p = kmalloc(second + sizeof(struct msgbuf), GFP_USER); -#endif if (!p) return -ENOMEM; err = -EFAULT; @@ -350,10 +355,6 @@ long compat_sys_msgrcv(int first, int se if (second < 0 || (second >= MAXBUF - sizeof(struct msgbuf))) return -EINVAL; -#ifdef CONFIG_ARCH_MIPS - second += 4; -#endif - if (!version) { struct compat_ipc_kludge __user *uipck = uptr; struct compat_ipc_kludge ipck; @@ -399,7 +400,7 @@ static inline int get_compat_msqid64(str return err; } -static inline int get_compat_msqid(struct msqid_ds *m, +static inline int get_compat_msqid(struct msqid64_ds *m, struct compat_msqid_ds __user *up) { int err; @@ -430,8 +431,8 @@ static inline int put_compat_msqid64_ds( return err; } -static inline int put_compat_msqid_ds(struct msqid_ds *m, - struct compat_msqid_ds __user *up) +static inline int put_compat_msqid_ds(struct msqid64_ds *m, + struct compat_msqid_ds __user *up) { int err; @@ -465,7 +466,6 @@ static inline int do_msgctl(int first, i long compat_sys_msgctl(int first, int second, void __user *uptr) { int err, err2; - struct msqid_ds m; struct msqid64_ds m64; int version = compat_ipc_parse_version(&second); @@ -479,29 +479,25 @@ long compat_sys_msgctl(int first, int se case IPC_SET: if (version == IPC_64) { err = get_compat_msqid64(&m64, uptr); - if (err) - break; - err = do_msgctl(first, second, &m64); } else { - err = get_compat_msqid(&m, uptr); - if (err) - break; - err = do_msgctl(first, second, &m); + err = get_compat_msqid(&m64, uptr); } + if (err) + break; + + err = do_msgctl(first, second, &m64); break; case IPC_STAT: case MSG_STAT: + err = do_msgctl(first, second, &m64); + if (err < 0) + break; + if (version == IPC_64) { - err = do_msgctl(first, second, &m64); - if (err < 0) - break; err2 = put_compat_msqid64_ds(&m64, uptr); } else { - err = do_msgctl(first, second, &m); - if (err < 0) - break; - err2 = put_compat_msqid_ds(&m, uptr); + err2 = put_compat_msqid_ds(&m64, uptr); } if (err2) err = -EFAULT; @@ -531,15 +527,15 @@ long compat_sys_shmat(int first, int sec } static inline int get_compat_shmid64_ds(struct shmid64_ds *s64, - struct compat_shmid64_ds __user *up64) + struct compat_shmid64_ds __user *up64) { if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) return -EFAULT; return __get_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); } -static inline int get_compat_shmid_ds(struct shmid_ds *s, - struct compat_shmid_ds __user *up) +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; @@ -547,7 +543,7 @@ static inline int get_compat_shmid_ds(st } static inline int put_compat_shmid64_ds(struct shmid64_ds *s64, - struct compat_shmid64_ds __user *up64) + struct compat_shmid64_ds __user *up64) { int err; @@ -564,8 +560,8 @@ static inline int put_compat_shmid64_ds( return err; } -static inline int put_compat_shmid_ds(struct shmid_ds *s, - struct compat_shmid_ds __user *up) +static inline int put_compat_shmid_ds(struct shmid64_ds *s, + struct compat_shmid_ds __user *up) { int err; @@ -582,8 +578,37 @@ static inline int put_compat_shmid_ds(st 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; + 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; + 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); +} + static inline int put_compat_shm_info(struct shm_info *si, - struct compat_shm_info __user *uip) + struct compat_shm_info __user *uip) { int err; @@ -613,47 +638,56 @@ static inline int do_shmctl(int shmid, i long compat_sys_shmctl(int first, int second, void __user *uptr) { - struct shmid_ds s; struct shmid64_ds s64; + struct shminfo64 smi; struct shm_info si; int err, err2; int version = compat_ipc_parse_version(&second); switch (second & (~IPC_64)) { - case IPC_INFO: - second &= ~IPC_64; /* So that we don't have to translate it */ case IPC_RMID: case SHM_LOCK: case SHM_UNLOCK: err = sys_shmctl(first, second, uptr); break; + case IPC_INFO: + err = do_shmctl(first, second, &smi); + if (err < 0) + break; + + 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(&s64, uptr); - if (err) - break; - err = do_shmctl(first, second, &s64); } else { - err = get_compat_shmid_ds(&s, uptr); - if (err) - break; - err = do_shmctl(first, second, &s); + err = get_compat_shmid_ds(&s64, uptr); } + if (err) + break; + + err = do_shmctl(first, second, &s64); break; case IPC_STAT: case SHM_STAT: + err = do_shmctl(first, second, &s64); + if (err < 0) + break; + if (version == IPC_64) { - err = do_shmctl(first, second, &s64); - if (err < 0) - break; err2 = put_compat_shmid64_ds(&s64, uptr); } else { - err = do_shmctl(first, second, &s); - if (err < 0) - break; - err2 = put_compat_shmid_ds(&s, uptr); + err2 = put_compat_shmid_ds(&s64, uptr); } if (err2) err = -EFAULT; _