From: Arnd Bergmann <arnd@arndb.de>

- 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;

_