From: Arnd Bergmann Adds a generic implementation of 32 bit emulation for IPC system calls. The code is based on the existing implementations for sparc64, ia64, mips, s390, ppc and x86_64, which can subsequently be converted to use this. --- include/linux/compat.h | 10 ipc/Makefile | 1 ipc/compat.c | 699 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 710 insertions(+) diff -puN include/linux/compat.h~compat-generic-ipc-emulation include/linux/compat.h --- 25/include/linux/compat.h~compat-generic-ipc-emulation 2004-02-18 12:04:42.000000000 -0800 +++ 25-akpm/include/linux/compat.h 2004-02-18 12:04:42.000000000 -0800 @@ -94,5 +94,15 @@ struct compat_dirent { char d_name[256]; }; +long compat_sys_semctl(int first, int second, int third, void __user *uptr); +long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); +long compat_sys_msgrcv(int first, int second, int msgtyp, int third, + int version, void __user *uptr); +long compat_sys_msgctl(int first, int second, void __user *uptr); +long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, + void __user *uptr); +long compat_sys_shmctl(int first, int second, void __user *uptr); +long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, + unsigned nsems, const struct compat_timespec __user *timeout); #endif /* CONFIG_COMPAT */ #endif /* _LINUX_COMPAT_H */ diff -puN /dev/null ipc/compat.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/ipc/compat.c 2004-02-18 12:04:42.000000000 -0800 @@ -0,0 +1,699 @@ +/* + * 32 bit compatibility code for System V IPC + * + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1999 Arun Sharma + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang + * Copyright (C) 2000 Gerhard Tonn (ton@de.ibm.com) + * Copyright (C) 2000-2002 Andi Kleen, SuSE Labs (x86-64 port) + * Copyright (C) 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 IBM + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 2004 Arnd Bergmann (arnd@arndb.de) + * + * This code is collected from the versions for sparc64, mips64, s390x, ia64, + * ppc64 and x86_64, all of which are based on the original sparc64 version + * by Jakub Jelinek. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "util.h" + +struct compat_msgbuf { + compat_long_t mtype; + char mtext[1]; +}; + +struct compat_ipc_perm { + key_t key; + compat_uid_t uid; + compat_gid_t gid; + compat_uid_t cuid; + compat_gid_t cgid; + compat_mode_t mode; + unsigned short seq; +}; + +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_msqid_ds { + struct compat_ipc_perm msg_perm; + compat_uptr_t msg_first; + compat_uptr_t msg_last; + compat_time_t msg_stime; + compat_time_t msg_rtime; + compat_time_t msg_ctime; + compat_ulong_t msg_lcbytes; + compat_ulong_t msg_lqbytes; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + compat_ipc_pid_t msg_lspid; + compat_ipc_pid_t msg_lrpid; +}; + +struct compat_shmid_ds { + struct compat_ipc_perm shm_perm; + int shm_segsz; + compat_time_t shm_atime; + compat_time_t shm_dtime; + compat_time_t shm_ctime; + compat_ipc_pid_t shm_cpid; + compat_ipc_pid_t shm_lpid; + unsigned short shm_nattch; + unsigned short shm_unused; + compat_uptr_t shm_unused2; + compat_uptr_t shm_unused3; +}; + +struct compat_ipc_kludge { + compat_uptr_t msgp; + compat_long_t msgtyp; +}; + +struct compat_shm_info { + compat_int_t used_ids; + compat_ulong_t shm_tot, shm_rss, shm_swp; + compat_ulong_t swap_attempts, swap_successes; +}; + +extern int sem_ctls[]; +#define sc_semopm (sem_ctls[2]) +#define MAXBUF (64*1024) + +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; +#endif + return IPC_64; + } else { + return IPC_OLD; + } +} +static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64, + struct compat_ipc64_perm *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 ipc_perm *p, + struct compat_ipc_perm *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 *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 ipc_perm *p, + struct compat_ipc_perm *up) +{ + int err; + compat_uid_t u; + compat_gid_t g; + + err = __put_user(p->key, &up->key); + SET_UID(u, p->uid); + err |= __put_user(u, &up->uid); + SET_GID(g, p->gid); + err |= __put_user(g, &up->gid); + SET_UID(u, p->cuid); + err |= __put_user(u, &up->cuid); + SET_GID(g, p->cgid); + err |= __put_user(g, &up->cgid); + err |= __put_user(p->mode, &up->mode); + err |= __put_user(p->seq, &up->seq); + return err; +} + +static inline int get_compat_semid64_ds(struct semid64_ds *s64, + struct compat_semid64_ds *up64) +{ + if (!access_ok (VERIFY_READ, up64, sizeof(*up64))) + return -EFAULT; + return __get_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); +} + +static inline int get_compat_semid_ds(struct semid_ds *s, + struct compat_semid_ds *up) +{ + if (!access_ok (VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm); +} + +static inline int put_compat_semid64_ds(struct semid64_ds *s64, + struct compat_semid64_ds *up64) +{ + int err; + + if (!access_ok (VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); + err |= __put_user(s64->sem_otime, &up64->sem_otime); + err |= __put_user(s64->sem_ctime, &up64->sem_ctime); + err |= __put_user(s64->sem_nsems, &up64->sem_nsems); + return err; +} + +static inline int put_compat_semid_ds(struct semid_ds *s, + struct compat_semid_ds *up) +{ + int err; + + if (!access_ok (VERIFY_WRITE, up, sizeof(*up))) + err = -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 inline int do_semctl(int semid, int semnum, int cmd, union semun arg) +{ + mm_segment_t old_fs; + int err; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_semctl(semid, semnum, cmd, arg); + set_fs(old_fs); + + return err; +} +long compat_sys_semctl(int first, int second, int third, void __user *uptr) +{ + union semun fourth; + u32 pad; + int err, err2; + struct semid64_ds s64; + struct semid_ds s; + int version = compat_ipc_parse_version(&third); + + if (!uptr) + return -EINVAL; + if (get_user(pad, (u32 __user *) uptr)) + return -EFAULT; + if ((third & (~IPC_64)) == SETVAL) + fourth.val = (int) pad; + else + fourth.__pad = 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: + 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)); + } + if (err2) + err = -EFAULT; + break; + + 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 = do_semctl(first, second, third, fourth); + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_msgsnd(int first, int second, int third, void __user *uptr) +{ + struct msgbuf *p; + struct compat_msgbuf __user *up; + mm_segment_t old_fs; + int err; + + if (first < 0) + return -EINVAL; + 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; + up = uptr; + if (get_user(p->mtype, &up->mtype) || + copy_from_user(p->mtext, &up->mtext, second)) + goto out; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgsnd(first, p, second, third); + set_fs(old_fs); +out: + kfree(p); + return err; +} + +long compat_sys_msgrcv(int first, int second, int msgtyp, int third, + int version, void __user *uptr) +{ + struct msgbuf *p; + struct compat_msgbuf __user *up; + mm_segment_t old_fs; + int err; + + if (first < 0) + return -EINVAL; + 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; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof(ipck))) + goto out; + uptr = compat_ptr(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc(second + sizeof(struct msgbuf), GFP_USER); + if (!p) + goto out; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgrcv(first, p, second, msgtyp, third); + set_fs(old_fs); + if (err < 0) + goto free_then_out; + up = uptr; + if (put_user(p->mtype, &up->mtype) || + __copy_to_user(&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree(p); +out: + return err; +} + +static inline int get_compat_msqid64(struct msqid64_ds *m64, + struct compat_msqid64_ds __user *up64) +{ + int err; + + if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) + return -EFAULT; + err = __get_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); + err |= __get_user(m64->msg_qbytes, &up64->msg_qbytes); + return err; +} + +static inline int get_compat_msqid(struct msqid_ds *m, + struct compat_msqid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + err = __get_compat_ipc_perm(&m->msg_perm, &up->msg_perm); + err |= __get_user(m->msg_qbytes, &up->msg_qbytes); + return err; +} + +static inline int put_compat_msqid64_ds(struct msqid64_ds *m64, + struct compat_msqid64_ds __user __user *up64) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); + err |= __put_user(m64->msg_stime, &up64->msg_stime); + err |= __put_user(m64->msg_rtime, &up64->msg_rtime); + err |= __put_user(m64->msg_ctime, &up64->msg_ctime); + err |= __put_user(m64->msg_cbytes, &up64->msg_cbytes); + err |= __put_user(m64->msg_qnum, &up64->msg_qnum); + err |= __put_user(m64->msg_qbytes, &up64->msg_qbytes); + err |= __put_user(m64->msg_lspid, &up64->msg_lspid); + err |= __put_user(m64->msg_lrpid, &up64->msg_lrpid); + return err; +} + +static inline int put_compat_msqid_ds(struct msqid_ds *m, + struct compat_msqid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + return -EFAULT; + err = __put_compat_ipc_perm(&m->msg_perm, &up->msg_perm); + err |= __put_user(m->msg_stime, &up->msg_stime); + err |= __put_user(m->msg_rtime, &up->msg_rtime); + err |= __put_user(m->msg_ctime, &up->msg_ctime); + err |= __put_user(m->msg_cbytes, &up->msg_cbytes); + err |= __put_user(m->msg_qnum, &up->msg_qnum); + err |= __put_user(m->msg_qbytes, &up->msg_qbytes); + err |= __put_user(m->msg_lspid, &up->msg_lspid); + err |= __put_user(m->msg_lrpid, &up->msg_lrpid); + return err; +} + +static inline int do_msgctl(int first, int second, void __user *buf) +{ + mm_segment_t old_fs; + int err; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgctl(first, second, buf); + set_fs(old_fs); + + return err; +} + +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); + + switch (second & (~IPC_64)) { + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl(first, second, uptr); + break; + + 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); + } + break; + + case IPC_STAT: + case MSG_STAT: + 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); + } + if (err2) + err = -EFAULT; + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, + void __user *uptr) +{ + int err; + unsigned long raddr; + compat_ulong_t __user *uaddr; + + if (version == 1) + return -EINVAL; + err = sys_shmat(first, uptr, second, &raddr); + if (err < 0) + return err; + uaddr = compat_ptr(third); + return put_user(raddr, uaddr); +} + +static inline int get_compat_shmid64_ds(struct shmid64_ds *s64, + 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) +{ + if (!access_ok(VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + return __get_compat_ipc_perm(&s->shm_perm, &up->shm_perm); +} + +static inline int put_compat_shmid64_ds(struct shmid64_ds *s64, + struct compat_shmid64_ds __user *up64) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); + err |= __put_user(s64->shm_atime, &up64->shm_atime); + err |= __put_user(s64->shm_dtime, &up64->shm_dtime); + err |= __put_user(s64->shm_ctime, &up64->shm_ctime); + err |= __put_user(s64->shm_segsz, &up64->shm_segsz); + err |= __put_user(s64->shm_nattch, &up64->shm_nattch); + err |= __put_user(s64->shm_cpid, &up64->shm_cpid); + err |= __put_user(s64->shm_lpid, &up64->shm_lpid); + return err; +} + +static inline int put_compat_shmid_ds(struct shmid_ds *s, + struct compat_shmid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + return -EFAULT; + err = __put_compat_ipc_perm(&s->shm_perm, &up->shm_perm); + err |= __put_user(s->shm_atime, &up->shm_atime); + err |= __put_user(s->shm_dtime, &up->shm_dtime); + err |= __put_user(s->shm_ctime, &up->shm_ctime); + err |= __put_user(s->shm_segsz, &up->shm_segsz); + err |= __put_user(s->shm_nattch, &up->shm_nattch); + err |= __put_user(s->shm_cpid, &up->shm_cpid); + err |= __put_user(s->shm_lpid, &up->shm_lpid); + return err; +} + +static inline int put_compat_shm_info(struct shm_info *si, + struct compat_shm_info __user *uip) +{ + int err; + + if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip))) + return -EFAULT; + err = __put_user(si->used_ids, &uip->used_ids); + err |= __put_user(si->shm_tot, &uip->shm_tot); + err |= __put_user(si->shm_rss, &uip->shm_rss); + err |= __put_user(si->shm_swp, &uip->shm_swp); + err |= __put_user(si->swap_attempts, &uip->swap_attempts); + err |= __put_user(si->swap_successes, &uip->swap_successes); + return err; +} + +static inline int do_shmctl(int shmid, int cmd, void *buf) +{ + mm_segment_t old_fs; + int err; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(shmid, cmd, buf); + set_fs(old_fs); + + return err; +} + +long compat_sys_shmctl(int first, int second, void __user *uptr) +{ + struct shmid_ds s; + struct shmid64_ds s64; + 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_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); + } + break; + + case IPC_STAT: + case SHM_STAT: + 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); + } + if (err2) + err = -EFAULT; + break; + + case SHM_INFO: + err = do_shmctl(first, second, &si); + if (err < 0) + break; + err2 = put_compat_shm_info(&si, uptr); + if (err2) + err = -EFAULT; + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, + unsigned nsops, const struct compat_timespec __user *timeout) +{ + struct timespec ts, __user *ts64; + + /* parameter checking precedence should mirror sys_semtimedop() */ + if (nsops < 1 || semid < 0) + return -EINVAL; + if (nsops > sc_semopm) + return -E2BIG; + if (!access_ok(VERIFY_READ, tsems, nsops * sizeof(struct sembuf))) + return -EFAULT; + if (!timeout) + return sys_semtimedop(semid, tsems, nsops, 0); + + ts64 = compat_alloc_user_space(sizeof(*ts64)); + if (get_compat_timespec(&ts, timeout)) + return -EFAULT; + if (copy_to_user(ts64, &ts, sizeof(ts))) + return -EFAULT; + + return sys_semtimedop(semid, tsems, nsops, ts64); +} diff -puN ipc/Makefile~compat-generic-ipc-emulation ipc/Makefile --- 25/ipc/Makefile~compat-generic-ipc-emulation 2004-02-18 12:04:42.000000000 -0800 +++ 25-akpm/ipc/Makefile 2004-02-18 12:04:42.000000000 -0800 @@ -4,4 +4,5 @@ obj-y := util.o +obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o obj-$(CONFIG_SYSVIPC) += msg.o sem.o shm.o _