Commit 05e43966 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle
Browse files

[MIPS] Use SYSVIPC_COMPAT to fix various problems on N32

N32 SysV IPC system calls should use 32-bit compatible code.
arch/mips/kernel/linux32.c have similar compatible code for O32, but
ipc/compat.c seems more complete.  We can use it for both N32 and O32.

This patch should fix these problems (and other possible problems):

http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=1149188824.6986.6.camel%40diimka-laptop
http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=44C6B829.8050508%40caviumnetworks.com

Signed-off-by: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 9567772f
......@@ -1940,6 +1940,11 @@ config COMPAT
depends on MIPS32_COMPAT
default y
config SYSVIPC_COMPAT
bool
depends on COMPAT && SYSVIPC
default y
config MIPS32_O32
bool "Kernel support for o32 binaries"
depends on MIPS32_COMPAT
......
......@@ -382,531 +382,6 @@ asmlinkage int sys32_sched_rr_get_interval(compat_pid_t pid,
return ret;
}
struct msgbuf32 { s32 mtype; char mtext[1]; };
struct ipc_perm32
{
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 ipc64_perm32 {
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;
unsigned short __pad1;
unsigned int __unused1;
unsigned int __unused2;
};
struct semid_ds32 {
struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */
compat_time_t sem_otime; /* last semop time */
compat_time_t sem_ctime; /* last change time */
u32 sem_base; /* ptr to first semaphore in array */
u32 sem_pending; /* pending operations to be processed */
u32 sem_pending_last; /* last pending operation */
u32 undo; /* undo requests on this array */
unsigned short sem_nsems; /* no. of semaphores in array */
};
struct semid64_ds32 {
struct ipc64_perm32 sem_perm;
compat_time_t sem_otime;
compat_time_t sem_ctime;
unsigned int sem_nsems;
unsigned int __unused1;
unsigned int __unused2;
};
struct msqid_ds32
{
struct ipc_perm32 msg_perm;
u32 msg_first;
u32 msg_last;
compat_time_t msg_stime;
compat_time_t msg_rtime;
compat_time_t msg_ctime;
u32 wwait;
u32 rwait;
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 msqid64_ds32 {
struct ipc64_perm32 msg_perm;
compat_time_t msg_stime;
unsigned int __unused1;
compat_time_t msg_rtime;
unsigned int __unused2;
compat_time_t msg_ctime;
unsigned int __unused3;
unsigned int msg_cbytes;
unsigned int msg_qnum;
unsigned int msg_qbytes;
compat_pid_t msg_lspid;
compat_pid_t msg_lrpid;
unsigned int __unused4;
unsigned int __unused5;
};
struct shmid_ds32 {
struct ipc_perm32 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;
};
struct shmid64_ds32 {
struct ipc64_perm32 shm_perm;
compat_size_t shm_segsz;
compat_time_t shm_atime;
compat_time_t shm_dtime;
compat_time_t shm_ctime;
compat_pid_t shm_cpid;
compat_pid_t shm_lpid;
unsigned int shm_nattch;
unsigned int __unused1;
unsigned int __unused2;
};
struct ipc_kludge32 {
u32 msgp;
s32 msgtyp;
};
static int
do_sys32_semctl(int first, int second, int third, void __user *uptr)
{
union semun fourth;
u32 pad;
int err, err2;
struct semid64_ds s;
mm_segment_t old_fs;
if (!uptr)
return -EINVAL;
err = -EFAULT;
if (get_user (pad, (u32 __user *)uptr))
return err;
if ((third & ~IPC_64) == SETVAL)
fourth.val = (int)pad;
else
fourth.__pad = (void __user *)A(pad);
switch (third & ~IPC_64) {
case IPC_INFO:
case IPC_RMID:
case IPC_SET:
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:
fourth.__pad = (struct semid64_ds __user *)&s;
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_semctl(first, second, third | IPC_64, fourth);
set_fs(old_fs);
if (third & IPC_64) {
struct semid64_ds32 __user *usp64 = (struct semid64_ds32 __user *) A(pad);
if (!access_ok(VERIFY_WRITE, usp64, sizeof(*usp64))) {
err = -EFAULT;
break;
}
err2 = __put_user(s.sem_perm.key, &usp64->sem_perm.key);
err2 |= __put_user(s.sem_perm.uid, &usp64->sem_perm.uid);
err2 |= __put_user(s.sem_perm.gid, &usp64->sem_perm.gid);
err2 |= __put_user(s.sem_perm.cuid, &usp64->sem_perm.cuid);
err2 |= __put_user(s.sem_perm.cgid, &usp64->sem_perm.cgid);
err2 |= __put_user(s.sem_perm.mode, &usp64->sem_perm.mode);
err2 |= __put_user(s.sem_perm.seq, &usp64->sem_perm.seq);
err2 |= __put_user(s.sem_otime, &usp64->sem_otime);
err2 |= __put_user(s.sem_ctime, &usp64->sem_ctime);
err2 |= __put_user(s.sem_nsems, &usp64->sem_nsems);
} else {
struct semid_ds32 __user *usp32 = (struct semid_ds32 __user *) A(pad);
if (!access_ok(VERIFY_WRITE, usp32, sizeof(*usp32))) {
err = -EFAULT;
break;
}
err2 = __put_user(s.sem_perm.key, &usp32->sem_perm.key);
err2 |= __put_user(s.sem_perm.uid, &usp32->sem_perm.uid);
err2 |= __put_user(s.sem_perm.gid, &usp32->sem_perm.gid);
err2 |= __put_user(s.sem_perm.cuid, &usp32->sem_perm.cuid);
err2 |= __put_user(s.sem_perm.cgid, &usp32->sem_perm.cgid);
err2 |= __put_user(s.sem_perm.mode, &usp32->sem_perm.mode);
err2 |= __put_user(s.sem_perm.seq, &usp32->sem_perm.seq);
err2 |= __put_user(s.sem_otime, &usp32->sem_otime);
err2 |= __put_user(s.sem_ctime, &usp32->sem_ctime);
err2 |= __put_user(s.sem_nsems, &usp32->sem_nsems);
}
if (err2)
err = -EFAULT;
break;
default:
err = - EINVAL;
break;
}
return err;
}
static int
do_sys32_msgsnd (int first, int second, int third, void __user *uptr)
{
struct msgbuf32 __user *up = (struct msgbuf32 __user *)uptr;
struct msgbuf *p;
mm_segment_t old_fs;
int err;
if (second < 0)
return -EINVAL;
p = kmalloc (second + sizeof (struct msgbuf)
+ 4, GFP_USER);
if (!p)
return -ENOMEM;
err = get_user (p->mtype, &up->mtype);
if (err)
goto out;
err |= __copy_from_user (p->mtext, &up->mtext, second);
if (err)
goto out;
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_msgsnd (first, (struct msgbuf __user *)p, second, third);
set_fs (old_fs);
out:
kfree (p);
return err;
}
static int
do_sys32_msgrcv (int first, int second, int msgtyp, int third,
int version, void __user *uptr)
{
struct msgbuf32 __user *up;
struct msgbuf *p;
mm_segment_t old_fs;
int err;
if (!version) {
struct ipc_kludge32 __user *uipck = (struct ipc_kludge32 __user *)uptr;
struct ipc_kludge32 ipck;
err = -EINVAL;
if (!uptr)
goto out;
err = -EFAULT;
if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge32)))
goto out;
uptr = (void __user *)AA(ipck.msgp);
msgtyp = ipck.msgtyp;
}
if (second < 0)
return -EINVAL;
err = -ENOMEM;
p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER);
if (!p)
goto out;
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_msgrcv (first, (struct msgbuf __user *)p, second + 4, msgtyp, third);
set_fs (old_fs);
if (err < 0)
goto free_then_out;
up = (struct msgbuf32 __user *)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 int
do_sys32_msgctl (int first, int second, void __user *uptr)
{
int err = -EINVAL, err2;
struct msqid64_ds m;
struct msqid_ds32 __user *up32 = (struct msqid_ds32 __user *)uptr;
struct msqid64_ds32 __user *up64 = (struct msqid64_ds32 __user *)uptr;
mm_segment_t old_fs;
switch (second & ~IPC_64) {
case IPC_INFO:
case IPC_RMID:
case MSG_INFO:
err = sys_msgctl (first, second, (struct msqid_ds __user *)uptr);
break;
case IPC_SET:
if (second & IPC_64) {
if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) {
err = -EFAULT;
break;
}
err = __get_user(m.msg_perm.uid, &up64->msg_perm.uid);
err |= __get_user(m.msg_perm.gid, &up64->msg_perm.gid);
err |= __get_user(m.msg_perm.mode, &up64->msg_perm.mode);
err |= __get_user(m.msg_qbytes, &up64->msg_qbytes);
} else {
if (!access_ok(VERIFY_READ, up32, sizeof(*up32))) {
err = -EFAULT;
break;
}
err = __get_user(m.msg_perm.uid, &up32->msg_perm.uid);
err |= __get_user(m.msg_perm.gid, &up32->msg_perm.gid);
err |= __get_user(m.msg_perm.mode, &up32->msg_perm.mode);
err |= __get_user(m.msg_qbytes, &up32->msg_qbytes);
}
if (err)
break;
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_msgctl(first, second | IPC_64, (struct msqid_ds __user *)&m);
set_fs(old_fs);
break;
case IPC_STAT:
case MSG_STAT:
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_msgctl(first, second | IPC_64, (struct msqid_ds __user *)&m);
set_fs(old_fs);
if (second & IPC_64) {
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) {
err = -EFAULT;
break;
}
err2 = __put_user(m.msg_perm.key, &up64->msg_perm.key);
err2 |= __put_user(m.msg_perm.uid, &up64->msg_perm.uid);
err2 |= __put_user(m.msg_perm.gid, &up64->msg_perm.gid);
err2 |= __put_user(m.msg_perm.cuid, &up64->msg_perm.cuid);
err2 |= __put_user(m.msg_perm.cgid, &up64->msg_perm.cgid);
err2 |= __put_user(m.msg_perm.mode, &up64->msg_perm.mode);
err2 |= __put_user(m.msg_perm.seq, &up64->msg_perm.seq);
err2 |= __put_user(m.msg_stime, &up64->msg_stime);
err2 |= __put_user(m.msg_rtime, &up64->msg_rtime);
err2 |= __put_user(m.msg_ctime, &up64->msg_ctime);
err2 |= __put_user(m.msg_cbytes, &up64->msg_cbytes);
err2 |= __put_user(m.msg_qnum, &up64->msg_qnum);
err2 |= __put_user(m.msg_qbytes, &up64->msg_qbytes);
err2 |= __put_user(m.msg_lspid, &up64->msg_lspid);
err2 |= __put_user(m.msg_lrpid, &up64->msg_lrpid);
if (err2)
err = -EFAULT;
} else {
if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) {
err = -EFAULT;
break;
}
err2 = __put_user(m.msg_perm.key, &up32->msg_perm.key);
err2 |= __put_user(m.msg_perm.uid, &up32->msg_perm.uid);
err2 |= __put_user(m.msg_perm.gid, &up32->msg_perm.gid);
err2 |= __put_user(m.msg_perm.cuid, &up32->msg_perm.cuid);
err2 |= __put_user(m.msg_perm.cgid, &up32->msg_perm.cgid);
err2 |= __put_user(m.msg_perm.mode, &up32->msg_perm.mode);
err2 |= __put_user(m.msg_perm.seq, &up32->msg_perm.seq);
err2 |= __put_user(m.msg_stime, &up32->msg_stime);
err2 |= __put_user(m.msg_rtime, &up32->msg_rtime);
err2 |= __put_user(m.msg_ctime, &up32->msg_ctime);
err2 |= __put_user(m.msg_cbytes, &up32->msg_cbytes);
err2 |= __put_user(m.msg_qnum, &up32->msg_qnum);
err2 |= __put_user(m.msg_qbytes, &up32->msg_qbytes);
err2 |= __put_user(m.msg_lspid, &up32->msg_lspid);
err2 |= __put_user(m.msg_lrpid, &up32->msg_lrpid);
if (err2)
err = -EFAULT;
}
break;
}
return err;
}
static int
do_sys32_shmat (int first, int second, int third, int version, void __user *uptr)
{
unsigned long raddr;
u32 __user *uaddr = (u32 __user *)A((u32)third);
int err = -EINVAL;
if (version == 1)
return err;
err = do_shmat (first, uptr, second, &raddr);
if (err)
return err;
err = put_user (raddr, uaddr);
return err;
}
struct shm_info32 {
int used_ids;
u32 shm_tot, shm_rss, shm_swp;
u32 swap_attempts, swap_successes;
};
static int
do_sys32_shmctl (int first, int second, void __user *uptr)
{
struct shmid64_ds32 __user *up64 = (struct shmid64_ds32 __user *)uptr;
struct shmid_ds32 __user *up32 = (struct shmid_ds32 __user *)uptr;
struct shm_info32 __user *uip = (struct shm_info32 __user *)uptr;
int err = -EFAULT, err2;
struct shmid64_ds s64;
mm_segment_t old_fs;
struct shm_info si;
struct shmid_ds s;
switch (second & ~IPC_64) {
case IPC_INFO:
second = IPC_INFO; /* So that we don't have to translate it */
case IPC_RMID:
case SHM_LOCK:
case SHM_UNLOCK:
err = sys_shmctl(first, second, (struct shmid_ds __user *)uptr);
break;
case IPC_SET:
if (second & IPC_64) {
err = get_user(s.shm_perm.uid, &up64->shm_perm.uid);
err |= get_user(s.shm_perm.gid, &up64->shm_perm.gid);
err |= get_user(s.shm_perm.mode, &up64->shm_perm.mode);
} else {
err = get_user(s.shm_perm.uid, &up32->shm_perm.uid);
err |= get_user(s.shm_perm.gid, &up32->shm_perm.gid);
err |= get_user(s.shm_perm.mode, &up32->shm_perm.mode);
}
if (err)
break;
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_shmctl(first, second & ~IPC_64, (struct shmid_ds __user *)&s);
set_fs(old_fs);
break;
case IPC_STAT:
case SHM_STAT:
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_shmctl(first, second | IPC_64, (void __user *) &s64);
set_fs(old_fs);
if (err < 0)
break;
if (second & IPC_64) {
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) {
err = -EFAULT;
break;
}
err2 = __put_user(s64.shm_perm.key, &up64->shm_perm.key);
err2 |= __put_user(s64.shm_perm.uid, &up64->shm_perm.uid);
err2 |= __put_user(s64.shm_perm.gid, &up64->shm_perm.gid);
err2 |= __put_user(s64.shm_perm.cuid, &up64->shm_perm.cuid);
err2 |= __put_user(s64.shm_perm.cgid, &up64->shm_perm.cgid);
err2 |= __put_user(s64.shm_perm.mode, &up64->shm_perm.mode);
err2 |= __put_user(s64.shm_perm.seq, &up64->shm_perm.seq);
err2 |= __put_user(s64.shm_atime, &up64->shm_atime);
err2 |= __put_user(s64.shm_dtime, &up64->shm_dtime);
err2 |= __put_user(s64.shm_ctime, &up64->shm_ctime);
err2 |= __put_user(s64.shm_segsz, &up64->shm_segsz);
err2 |= __put_user(s64.shm_nattch, &up64->shm_nattch);
err2 |= __put_user(s64.shm_cpid, &up64->shm_cpid);
err2 |= __put_user(s64.shm_lpid, &up64->shm_lpid);
} else {
if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) {
err = -EFAULT;
break;
}
err2 = __put_user(s64.shm_perm.key, &up32->shm_perm.key);
err2 |= __put_user(s64.shm_perm.uid, &up32->shm_perm.uid);
err2 |= __put_user(s64.shm_perm.gid, &up32->shm_perm.gid);
err2 |= __put_user(s64.shm_perm.cuid, &up32->shm_perm.cuid);
err2 |= __put_user(s64.shm_perm.cgid, &up32->shm_perm.cgid);
err2 |= __put_user(s64.shm_perm.mode, &up32->shm_perm.mode);
err2 |= __put_user(s64.shm_perm.seq, &up32->shm_perm.seq);
err2 |= __put_user(s64.shm_atime, &up32->shm_atime);
err2 |= __put_user(s64.shm_dtime, &up32->shm_dtime);
err2 |= __put_user(s64.shm_ctime, &up32->shm_ctime);
err2 |= __put_user(s64.shm_segsz, &up32->shm_segsz);
err2 |= __put_user(s64.shm_nattch, &up32->shm_nattch);
err2 |= __put_user(s64.shm_cpid, &up32->shm_cpid);
err2 |= __put_user(s64.shm_lpid, &up32->shm_lpid);
}
if (err2)
err = -EFAULT;
break;
case SHM_INFO:
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_shmctl(first, second, (void __user *)&si);
set_fs(old_fs);
if (err < 0)
break;
err2 = put_user(si.used_ids, &uip->used_ids);
err2 |= __put_user(si.shm_tot, &uip->shm_tot);
err2 |= __put_user(si.shm_rss, &uip->shm_rss);
err2 |= __put_user(si.shm_swp, &uip->shm_swp);
err2 |= __put_user(si.swap_attempts, &uip->swap_attempts);
err2 |= __put_user (si.swap_successes, &uip->swap_successes);
if (err2)
err = -EFAULT;
break;
default:
err = -EINVAL;
break;
}
return err;
}
static int sys32_semtimedop(int semid, struct sembuf __user *tsems, int nsems,
const struct compat_timespec __user *timeout32)
{
struct compat_timespec t32;
struct timespec __user *t64 = compat_alloc_user_space(sizeof(*t64));
if (copy_from_user(&t32, timeout32, sizeof(t32)))
return -EFAULT;
if (put_user(t32.tv_sec, &t64->tv_sec) ||
put_user(t32.tv_nsec, &t64->tv_nsec))
return -EFAULT;
return sys_semtimedop(semid, tsems, nsems, t64);
}
asmlinkage long
sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
{
......@@ -918,48 +393,43 @@ sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
switch (call) {
case SEMOP:
/* struct sembuf is the same on 32 and 64bit :)) */
err = sys_semtimedop (first, (struct sembuf __user *)AA(ptr), second,
NULL);
err = sys_semtimedop(first, compat_ptr(ptr), second, NULL);
break;
case SEMTIMEDOP:
err = sys32_semtimedop (first, (struct sembuf __user *)AA(ptr), second,
(const struct compat_timespec __user *)AA(fifth));
err = compat_sys_semtimedop(first, compat_ptr(ptr), second,
compat_ptr(fifth));
break;
case SEMGET:
err = sys_semget (first, second, third);
err = sys_semget(first, second, third);
break;
case SEMCTL:
err = do_sys32_semctl (first, second, third,
(void __user *)AA(ptr));
err = compat_sys_semctl(first, second, third, compat_ptr(ptr));
break;
case MSGSND:
err = do_sys32_msgsnd (first, second, third,
(void __user *)AA(ptr));
err = compat_sys_msgsnd(first, second, third, compat_ptr(ptr));
break;
case MSGRCV:
err = do_sys32_msgrcv (first, second, fifth, third,
version, (void __user *)AA(ptr));
err = compat_sys_msgrcv(first, second, fifth, third,
version, compat_ptr(ptr));
break;
case MSGGET:
err = sys_msgget ((key_t) first, second);
err = sys_msgget((key_t) first, second);
break;
case MSGCTL:
err = do_sys32_msgctl (first, second, (void __user *)AA(ptr));
err = compat_sys_msgctl(first, second, compat_ptr(ptr));
break;
case SHMAT:
err = do_sys32_shmat (first, second, third,
version, (void __user *)AA(ptr));