Commit 76a9bdec authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] generic 32 bit emulation for System-V IPC

From: Arnd Bergmann <arnd@arndb.de>

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.
parent 5299b8f7
...@@ -537,7 +537,11 @@ config DEBUG_INFO ...@@ -537,7 +537,11 @@ config DEBUG_INFO
debugging info resulting in a larger kernel image. debugging info resulting in a larger kernel image.
Say Y here only if you plan to use gdb to debug the kernel. Say Y here only if you plan to use gdb to debug the kernel.
If you don't debug the kernel, you can say N. If you don't debug the kernel, you can say N.
config SYSVIPC_COMPAT
bool
depends on COMPAT && SYSVIPC
default y
endmenu endmenu
source "security/Kconfig" source "security/Kconfig"
......
...@@ -1023,143 +1023,6 @@ sys32_writev (int fd, struct compat_iovec *vector, u32 count) ...@@ -1023,143 +1023,6 @@ sys32_writev (int fd, struct compat_iovec *vector, u32 count)
return ret; return ret;
} }
/*
* sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation..
*
* This is really horribly ugly.
*/
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_uid32_t uid;
compat_gid32_t gid;
compat_uid32_t cuid;
compat_gid32_t cgid;
compat_mode_t mode;
unsigned short __pad1;
unsigned short seq;
unsigned short __pad2;
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;
unsigned int __unused1;
compat_time_t sem_ctime;
unsigned int __unused2;
unsigned int sem_nsems;
unsigned int __unused3;
unsigned int __unused4;
};
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;
unsigned int __unused1;
compat_time_t shm_dtime;
unsigned int __unused2;
compat_time_t shm_ctime;
unsigned int __unused3;
compat_pid_t shm_cpid;
compat_pid_t shm_lpid;
unsigned int shm_nattch;
unsigned int __unused4;
unsigned int __unused5;
};
struct shminfo64_32 {
unsigned int shmmax;
unsigned int shmmin;
unsigned int shmmni;
unsigned int shmseg;
unsigned int shmall;
unsigned int __unused1;
unsigned int __unused2;
unsigned int __unused3;
unsigned int __unused4;
};
struct shm_info32 {
int used_ids;
u32 shm_tot, shm_rss, shm_swp;
u32 swap_attempts, swap_successes;
};
struct ipc_kludge {
u32 msgp;
s32 msgtyp;
};
#define SEMOP 1 #define SEMOP 1
#define SEMGET 2 #define SEMGET 2
#define SEMCTL 3 #define SEMCTL 3
...@@ -1173,454 +1036,6 @@ struct ipc_kludge { ...@@ -1173,454 +1036,6 @@ struct ipc_kludge {
#define SHMGET 23 #define SHMGET 23
#define SHMCTL 24 #define SHMCTL 24
#define IPCOP_MASK(__x) (1UL << (__x))
static int
ipc_parse_version32 (int *cmd)
{
if (*cmd & IPC_64) {
*cmd ^= IPC_64;
return IPC_64;
} else {
return IPC_OLD;
}
}
static int
semctl32 (int first, int second, int third, void *uptr)
{
union semun fourth;
u32 pad;
int err = 0, err2;
struct semid64_ds s;
mm_segment_t old_fs;
int version = ipc_parse_version32(&third);
if (!uptr)
return -EINVAL;
if (get_user(pad, (u32 *)uptr))
return -EFAULT;
if (third == SETVAL)
fourth.val = (int)pad;
else
fourth.__pad = (void *)A(pad);
switch (third) {
default:
err = -EINVAL;
break;
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 = &s;
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_semctl(first, second, third, fourth);
set_fs(old_fs);
if (version == IPC_64) {
struct semid64_ds32 *usp64 = (struct semid64_ds32 *) 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 *usp32 = (struct semid_ds32 *) 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;
}
return err;
}
static int
do_sys32_msgsnd (int first, int second, int third, void *uptr)
{
struct msgbuf *p = kmalloc(second + sizeof(struct msgbuf), GFP_USER);
struct msgbuf32 *up = (struct msgbuf32 *)uptr;
mm_segment_t old_fs;
int err;
if (!p)
return -ENOMEM;
err = get_user(p->mtype, &up->mtype);
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, 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 *uptr)
{
struct msgbuf32 *up;
struct msgbuf *p;
mm_segment_t old_fs;
int err;
if (!version) {
struct ipc_kludge *uipck = (struct ipc_kludge *)uptr;
struct ipc_kludge ipck;
err = -EINVAL;
if (!uptr)
goto out;
err = -EFAULT;
if (copy_from_user(&ipck, uipck, sizeof(struct ipc_kludge)))
goto out;
uptr = (void *)A(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 = (struct msgbuf32 *)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
msgctl32 (int first, int second, void *uptr)
{
int err = -EINVAL, err2;
struct msqid64_ds m64;
struct msqid_ds32 *up32 = (struct msqid_ds32 *)uptr;
struct msqid64_ds32 *up64 = (struct msqid64_ds32 *)uptr;
mm_segment_t old_fs;
int version = ipc_parse_version32(&second);
switch (second) {
case IPC_INFO:
case IPC_RMID:
case MSG_INFO:
err = sys_msgctl(first, second, (struct msqid_ds *)uptr);
break;
case IPC_SET:
if (version == IPC_64) {
err = get_user(m64.msg_perm.uid, &up64->msg_perm.uid);
err |= get_user(m64.msg_perm.gid, &up64->msg_perm.gid);
err |= get_user(m64.msg_perm.mode, &up64->msg_perm.mode);
err |= get_user(m64.msg_qbytes, &up64->msg_qbytes);
} else {
err = get_user(m64.msg_perm.uid, &up32->msg_perm.uid);
err |= get_user(m64.msg_perm.gid, &up32->msg_perm.gid);
err |= get_user(m64.msg_perm.mode, &up32->msg_perm.mode);
err |= get_user(m64.msg_qbytes, &up32->msg_qbytes);
}
if (err)
break;
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_msgctl(first, second, (struct msqid_ds *)&m64);
set_fs(old_fs);
break;
case IPC_STAT:
case MSG_STAT:
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_msgctl(first, second, (struct msqid_ds *)&m64);
set_fs(old_fs);
if (version == IPC_64) {
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) {
err = -EFAULT;
break;
}
err2 = __put_user(m64.msg_perm.key, &up64->msg_perm.key);
err2 |= __put_user(m64.msg_perm.uid, &up64->msg_perm.uid);
err2 |= __put_user(m64.msg_perm.gid, &up64->msg_perm.gid);
err2 |= __put_user(m64.msg_perm.cuid, &up64->msg_perm.cuid);
err2 |= __put_user(m64.msg_perm.cgid, &up64->msg_perm.cgid);
err2 |= __put_user(m64.msg_perm.mode, &up64->msg_perm.mode);
err2 |= __put_user(m64.msg_perm.seq, &up64->msg_perm.seq);
err2 |= __put_user(m64.msg_stime, &up64->msg_stime);
err2 |= __put_user(m64.msg_rtime, &up64->msg_rtime);
err2 |= __put_user(m64.msg_ctime, &up64->msg_ctime);
err2 |= __put_user(m64.msg_cbytes, &up64->msg_cbytes);
err2 |= __put_user(m64.msg_qnum, &up64->msg_qnum);
err2 |= __put_user(m64.msg_qbytes, &up64->msg_qbytes);
err2 |= __put_user(m64.msg_lspid, &up64->msg_lspid);
err2 |= __put_user(m64.msg_lrpid, &up64->msg_lrpid);
if (err2)
err = -EFAULT;
} else {
if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) {
err = -EFAULT;
break;
}
err2 = __put_user(m64.msg_perm.key, &up32->msg_perm.key);
err2 |= __put_user(m64.msg_perm.uid, &up32->msg_perm.uid);
err2 |= __put_user(m64.msg_perm.gid, &up32->msg_perm.gid);
err2 |= __put_user(m64.msg_perm.cuid, &up32->msg_perm.cuid);
err2 |= __put_user(m64.msg_perm.cgid, &up32->msg_perm.cgid);
err2 |= __put_user(m64.msg_perm.mode, &up32->msg_perm.mode);
err2 |= __put_user(m64.msg_perm.seq, &up32->msg_perm.seq);
err2 |= __put_user(m64.msg_stime, &up32->msg_stime);
err2 |= __put_user(m64.msg_rtime, &up32->msg_rtime);
err2 |= __put_user(m64.msg_ctime, &up32->msg_ctime);
err2 |= __put_user(m64.msg_cbytes, &up32->msg_cbytes);
err2 |= __put_user(m64.msg_qnum, &up32->msg_qnum);
err2 |= __put_user(m64.msg_qbytes, &up32->msg_qbytes);
err2 |= __put_user(m64.msg_lspid, &up32->msg_lspid);
err2 |= __put_user(m64.msg_lrpid, &up32->msg_lrpid);
if (err2)
err = -EFAULT;
}
break;
}
return err;
}
static int
shmat32 (int first, int second, int third, int version, void *uptr)
{
unsigned long raddr;
u32 *uaddr = (u32 *)A((u32)third);
int err;
if (version == 1)
return -EINVAL; /* iBCS2 emulator entry point: unsupported */
err = do_shmat(first, uptr, second, &raddr);
if (err)
return err;
return put_user(raddr, uaddr);
}
static int
shmctl32 (int first, int second, void *uptr)
{
int err = -EFAULT, err2;
struct shmid64_ds s64;
struct shmid_ds32 *up32 = (struct shmid_ds32 *)uptr;
struct shmid64_ds32 *up64 = (struct shmid64_ds32 *)uptr;
mm_segment_t old_fs;
struct shm_info32 *uip = (struct shm_info32 *)uptr;
struct shm_info si;
int version = ipc_parse_version32(&second);
struct shminfo64 smi;
struct shminfo *usi32 = (struct shminfo *) uptr;
struct shminfo64_32 *usi64 = (struct shminfo64_32 *) uptr;
switch (second) {
case IPC_INFO:
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_shmctl(first, second, (struct shmid_ds *)&smi);
set_fs(old_fs);
if (version == IPC_64) {
if (!access_ok(VERIFY_WRITE, usi64, sizeof(*usi64))) {
err = -EFAULT;
break;
}
err2 = __put_user(smi.shmmax, &usi64->shmmax);
err2 |= __put_user(smi.shmmin, &usi64->shmmin);
err2 |= __put_user(smi.shmmni, &usi64->shmmni);
err2 |= __put_user(smi.shmseg, &usi64->shmseg);
err2 |= __put_user(smi.shmall, &usi64->shmall);
} else {
if (!access_ok(VERIFY_WRITE, usi32, sizeof(*usi32))) {
err = -EFAULT;
break;
}
err2 = __put_user(smi.shmmax, &usi32->shmmax);
err2 |= __put_user(smi.shmmin, &usi32->shmmin);
err2 |= __put_user(smi.shmmni, &usi32->shmmni);
err2 |= __put_user(smi.shmseg, &usi32->shmseg);
err2 |= __put_user(smi.shmall, &usi32->shmall);
}
if (err2)
err = -EFAULT;
break;
case IPC_RMID:
case SHM_LOCK:
case SHM_UNLOCK:
err = sys_shmctl(first, second, (struct shmid_ds *)uptr);
break;
case IPC_SET:
if (version == IPC_64) {
err = get_user(s64.shm_perm.uid, &up64->shm_perm.uid);
err |= get_user(s64.shm_perm.gid, &up64->shm_perm.gid);
err |= get_user(s64.shm_perm.mode, &up64->shm_perm.mode);
} else {
err = get_user(s64.shm_perm.uid, &up32->shm_perm.uid);
err |= get_user(s64.shm_perm.gid, &up32->shm_perm.gid);
err |= get_user(s64.shm_perm.mode, &up32->shm_perm.mode);
}
if (err)
break;
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_shmctl(first, second, (struct shmid_ds *)&s64);
set_fs(old_fs);
break;
case IPC_STAT:
case SHM_STAT:
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_shmctl(first, second, (struct shmid_ds *)&s64);
set_fs(old_fs);
if (err < 0)
break;
if (version == 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 *)&si);
set_fs(old_fs);
if (err < 0)
break;
if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip))) {
err = -EFAULT;
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;
}
return err;
}
extern int sem_ctls[];
#define sc_semopm (sem_ctls[2])
static long
semtimedop32(int semid, struct sembuf *tsops, int nsops,
struct compat_timespec *timeout32)
{
struct timespec t;
mm_segment_t oldfs;
long ret;
/* 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, tsops, nsops * sizeof(struct sembuf)) ||
get_compat_timespec(&t, timeout32))
return -EFAULT;
oldfs = get_fs();
set_fs(KERNEL_DS);
ret = sys_semtimedop(semid, tsops, nsops, &t);
set_fs(oldfs);
return ret;
}
asmlinkage long asmlinkage long
sys32_ipc(u32 call, int first, int second, int third, u32 ptr, u32 fifth) sys32_ipc(u32 call, int first, int second, int third, u32 ptr, u32 fifth)
{ {
...@@ -1632,36 +1047,36 @@ sys32_ipc(u32 call, int first, int second, int third, u32 ptr, u32 fifth) ...@@ -1632,36 +1047,36 @@ sys32_ipc(u32 call, int first, int second, int third, u32 ptr, u32 fifth)
switch (call) { switch (call) {
case SEMTIMEDOP: case SEMTIMEDOP:
if (fifth) if (fifth)
return semtimedop32(first, (struct sembuf *)AA(ptr), return compat_sys_semtimedop(first, compat_ptr(ptr),
second, (struct compat_timespec *)AA(fifth)); second, compat_ptr(fifth));
/* else fall through for normal semop() */ /* else fall through for normal semop() */
case SEMOP: case SEMOP:
/* struct sembuf is the same on 32 and 64bit :)) */ /* struct sembuf is the same on 32 and 64bit :)) */
return sys_semtimedop(first, (struct sembuf *)AA(ptr), second, return sys_semtimedop(first, compat_ptr(ptr), second,
NULL); NULL);
case SEMGET: case SEMGET:
return sys_semget(first, second, third); return sys_semget(first, second, third);
case SEMCTL: case SEMCTL:
return semctl32(first, second, third, (void *)AA(ptr)); return compat_sys_semctl(first, second, third, compat_ptr(ptr));
case MSGSND: case MSGSND:
return do_sys32_msgsnd(first, second, third, (void *)AA(ptr)); return compat_sys_msgsnd(first, second, third, compat_ptr(ptr));
case MSGRCV: case MSGRCV:
return do_sys32_msgrcv(first, second, fifth, third, version, (void *)AA(ptr)); return compat_sys_msgrcv(first, second, fifth, third, version, compat_ptr(ptr));
case MSGGET: case MSGGET:
return sys_msgget((key_t) first, second); return sys_msgget((key_t) first, second);
case MSGCTL: case MSGCTL:
return msgctl32(first, second, (void *)AA(ptr)); return compat_sys_msgctl(first, second, compat_ptr(ptr));
case SHMAT: case SHMAT:
return shmat32(first, second, third, version, (void *)AA(ptr)); return compat_sys_shmat(first, second, third, version, compat_ptr(ptr));
break; break;
case SHMDT: case SHMDT:
return sys_shmdt((char *)AA(ptr)); return sys_shmdt(compat_ptr(ptr));
case SHMGET: case SHMGET:
return sys_shmget(first, second, third); return sys_shmget(first, second, third);
case SHMCTL: case SHMCTL:
return shmctl32(first, second, (void *)AA(ptr)); return compat_sys_shmctl(first, second, compat_ptr(ptr));
default: default:
return -ENOSYS; return -ENOSYS;
......
...@@ -143,6 +143,11 @@ config COMPAT ...@@ -143,6 +143,11 @@ config COMPAT
depends on S390_SUPPORT depends on S390_SUPPORT
default y default y
config SYSVIPC_COMPAT
bool
depends on COMPAT && SYSVIPC
default y
config BINFMT_ELF32 config BINFMT_ELF32
tristate "Kernel support for 31 bit ELF binaries" tristate "Kernel support for 31 bit ELF binaries"
depends on S390_SUPPORT depends on S390_SUPPORT
......
...@@ -293,541 +293,6 @@ static inline long put_tv32(struct compat_timeval *o, struct timeval *i) ...@@ -293,541 +293,6 @@ static inline long put_tv32(struct compat_timeval *o, struct timeval *i)
__put_user(i->tv_usec, &o->tv_usec))); __put_user(i->tv_usec, &o->tv_usec)));
} }
struct msgbuf32 { s32 mtype; char mtext[1]; };
struct ipc64_perm_ds32
{
__kernel_key_t key;
__kernel_uid32_t uid;
__kernel_gid32_t gid;
__kernel_uid32_t cuid;
__kernel_gid32_t cgid;
compat_mode_t mode;
unsigned short __pad1;
unsigned short seq;
unsigned short __pad2;
unsigned int __unused1;
unsigned int __unused2;
};
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 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_perm_ds32 sem_perm;
unsigned int __pad1;
compat_time_t sem_otime;
unsigned int __pad2;
compat_time_t sem_ctime;
u32 sem_nsems;
u32 __unused1;
u32 __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_perm_ds32 msg_perm;
unsigned int __pad1;
compat_time_t msg_stime;
unsigned int __pad2;
compat_time_t msg_rtime;
unsigned int __pad3;
compat_time_t msg_ctime;
unsigned int msg_cbytes;
unsigned int msg_qnum;
unsigned int msg_qbytes;
compat_pid_t msg_lspid;
compat_pid_t msg_lrpid;
unsigned int __unused1;
unsigned int __unused2;
};
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_perm_ds32 shm_perm;
compat_size_t shm_segsz;
compat_time_t shm_atime;
unsigned int __unused1;
compat_time_t shm_dtime;
unsigned int __unused2;
compat_time_t shm_ctime;
unsigned int __unused3;
compat_pid_t shm_cpid;
compat_pid_t shm_lpid;
unsigned int shm_nattch;
unsigned int __unused4;
unsigned int __unused5;
};
extern int sem_ctls[];
#define sc_semopm (sem_ctls[2])
#define SEMOPM_FAST 64 /* ~ 372 bytes on stack */
static long
do_sys32_semtimedop (int semid, struct sembuf *tsops, int nsops,
struct compat_timespec *timeout32)
{
struct sembuf *sops, fast_sops[SEMOPM_FAST];
struct timespec t;
mm_segment_t oldfs;
long ret;
/* parameter checking precedence should mirror sys_semtimedop() */
if (nsops < 1 || semid < 0)
return -EINVAL;
if (nsops > sc_semopm)
return -E2BIG;
if (nsops <= SEMOPM_FAST)
sops = fast_sops;
else {
sops = kmalloc(nsops * sizeof(*sops), GFP_KERNEL);
if (sops == NULL)
return -ENOMEM;
}
if (copy_from_user(sops, tsops, nsops * sizeof(*tsops)) ||
get_compat_timespec(&t, timeout32))
ret = -EFAULT;
else {
oldfs = get_fs();
set_fs(KERNEL_DS);
ret = sys_semtimedop(semid, sops, nsops, &t);
set_fs(oldfs);
}
if (sops != fast_sops)
kfree(sops);
return ret;
}
#define IPCOP_MASK(__x) (1UL << (__x))
static int do_sys32_semctl(int first, int second, int third, void *uptr)
{
union semun fourth;
u32 pad;
int err = -EINVAL;
if (!uptr)
goto out;
err = -EFAULT;
if (get_user (pad, (u32 *)uptr))
goto out;
if(third == SETVAL)
fourth.val = (int)pad;
else
fourth.__pad = (void *)A(pad);
if (IPCOP_MASK (third) &
(IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) | IPCOP_MASK (GETVAL) |
IPCOP_MASK (GETPID) | IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) |
IPCOP_MASK (GETALL) | IPCOP_MASK (SETALL) | IPCOP_MASK (IPC_RMID))) {
err = sys_semctl (first, second, third, fourth);
} else if (third & IPC_64) {
struct semid64_ds s;
struct semid64_ds32 *usp = (struct semid64_ds32 *)A(pad);
mm_segment_t old_fs;
int need_back_translation;
if (third == (IPC_SET|IPC_64)) {
err = get_user (s.sem_perm.uid, &usp->sem_perm.uid);
err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid);
err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode);
if (err)
goto out;
fourth.__pad = &s;
}
need_back_translation =
(IPCOP_MASK (third) &
(IPCOP_MASK (SEM_STAT) | IPCOP_MASK (IPC_STAT))) != 0;
if (need_back_translation)
fourth.__pad = &s;
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_semctl (first, second, third, fourth);
set_fs (old_fs);
if (need_back_translation) {
int err2 = put_user (s.sem_perm.key, &usp->sem_perm.key);
err2 |= __put_user (high2lowuid(s.sem_perm.uid), &usp->sem_perm.uid);
err2 |= __put_user (high2lowgid(s.sem_perm.gid), &usp->sem_perm.gid);
err2 |= __put_user (high2lowuid(s.sem_perm.cuid), &usp->sem_perm.cuid);
err2 |= __put_user (high2lowgid(s.sem_perm.cgid), &usp->sem_perm.cgid);
err2 |= __put_user (s.sem_perm.mode, &usp->sem_perm.mode);
err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq);
err2 |= __put_user (s.sem_otime, &usp->sem_otime);
err2 |= __put_user (s.sem_ctime, &usp->sem_ctime);
err2 |= __put_user (s.sem_nsems, &usp->sem_nsems);
if (err2) err = -EFAULT;
}
} else {
struct semid_ds s;
struct semid_ds32 *usp = (struct semid_ds32 *)A(pad);
mm_segment_t old_fs;
int need_back_translation;
if (third == IPC_SET) {
err = get_user (s.sem_perm.uid, &usp->sem_perm.uid);
err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid);
err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode);
if (err)
goto out;
fourth.__pad = &s;
}
need_back_translation =
(IPCOP_MASK (third) &
(IPCOP_MASK (SEM_STAT) | IPCOP_MASK (IPC_STAT))) != 0;
if (need_back_translation)
fourth.__pad = &s;
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_semctl (first, second, third, fourth);
set_fs (old_fs);
if (need_back_translation) {
int err2 = put_user (s.sem_perm.key, &usp->sem_perm.key);
err2 |= __put_user (high2lowuid(s.sem_perm.uid), &usp->sem_perm.uid);
err2 |= __put_user (high2lowgid(s.sem_perm.gid), &usp->sem_perm.gid);
err2 |= __put_user (high2lowuid(s.sem_perm.cuid), &usp->sem_perm.cuid);
err2 |= __put_user (high2lowgid(s.sem_perm.cgid), &usp->sem_perm.cgid);
err2 |= __put_user (s.sem_perm.mode, &usp->sem_perm.mode);
err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq);
err2 |= __put_user (s.sem_otime, &usp->sem_otime);
err2 |= __put_user (s.sem_ctime, &usp->sem_ctime);
err2 |= __put_user (s.sem_nsems, &usp->sem_nsems);
if (err2) err = -EFAULT;
}
}
out:
return err;
}
static int do_sys32_msgsnd (int first, int second, int third, void *uptr)
{
struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf), GFP_USER);
struct msgbuf32 *up = (struct msgbuf32 *)uptr;
mm_segment_t old_fs;
int err;
if (!p)
return -ENOMEM;
err = -EINVAL;
if (second > MSGMAX || first < 0 || second < 0)
goto out;
err = -EFAULT;
if (!uptr)
goto out;
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;
}
static int do_sys32_msgrcv (int first, int second, int msgtyp, int third,
int version, void *uptr)
{
struct msgbuf32 *up;
struct msgbuf *p;
mm_segment_t old_fs;
int err;
if (first < 0 || second < 0)
return -EINVAL;
if (!version) {
struct ipc_kludge_32 *uipck = (struct ipc_kludge_32 *)uptr;
struct ipc_kludge_32 ipck;
err = -EINVAL;
if (!uptr)
goto out;
err = -EFAULT;
if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge_32)))
goto out;
uptr = (void *)A(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 = (struct msgbuf32 *)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 *uptr)
{
int err;
if (IPCOP_MASK (second) &
(IPCOP_MASK (IPC_INFO) | IPCOP_MASK (MSG_INFO) |
IPCOP_MASK (IPC_RMID))) {
err = sys_msgctl (first, second, (struct msqid_ds *)uptr);
} else if (second & IPC_64) {
struct msqid64_ds m;
struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr;
mm_segment_t old_fs;
if (second == (IPC_SET|IPC_64)) {
err = get_user (m.msg_perm.uid, &up->msg_perm.uid);
err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid);
err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode);
err |= __get_user (m.msg_qbytes, &up->msg_qbytes);
if (err)
goto out;
}
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_msgctl (first, second, (struct msqid_ds *)&m);
set_fs (old_fs);
if (IPCOP_MASK (second) &
(IPCOP_MASK (MSG_STAT) | IPCOP_MASK (IPC_STAT))) {
int err2 = put_user (m.msg_perm.key, &up->msg_perm.key);
err2 |= __put_user (high2lowuid(m.msg_perm.uid), &up->msg_perm.uid);
err2 |= __put_user (high2lowgid(m.msg_perm.gid), &up->msg_perm.gid);
err2 |= __put_user (high2lowuid(m.msg_perm.cuid), &up->msg_perm.cuid);
err2 |= __put_user (high2lowgid(m.msg_perm.cgid), &up->msg_perm.cgid);
err2 |= __put_user (m.msg_perm.mode, &up->msg_perm.mode);
err2 |= __put_user (m.msg_perm.seq, &up->msg_perm.seq);
err2 |= __put_user (m.msg_stime, &up->msg_stime);
err2 |= __put_user (m.msg_rtime, &up->msg_rtime);
err2 |= __put_user (m.msg_ctime, &up->msg_ctime);
err2 |= __put_user (m.msg_cbytes, &up->msg_cbytes);
err2 |= __put_user (m.msg_qnum, &up->msg_qnum);
err2 |= __put_user (m.msg_qbytes, &up->msg_qbytes);
err2 |= __put_user (m.msg_lspid, &up->msg_lspid);
err2 |= __put_user (m.msg_lrpid, &up->msg_lrpid);
if (err2)
err = -EFAULT;
}
} else {
struct msqid_ds m;
struct msqid_ds32 *up = (struct msqid_ds32 *)uptr;
mm_segment_t old_fs;
if (second == IPC_SET) {
err = get_user (m.msg_perm.uid, &up->msg_perm.uid);
err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid);
err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode);
err |= __get_user (m.msg_qbytes, &up->msg_qbytes);
if (err)
goto out;
}
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_msgctl (first, second, &m);
set_fs (old_fs);
if (IPCOP_MASK (second) &
(IPCOP_MASK (MSG_STAT) | IPCOP_MASK (IPC_STAT))) {
int err2 = put_user (m.msg_perm.key, &up->msg_perm.key);
err2 |= __put_user (high2lowuid(m.msg_perm.uid), &up->msg_perm.uid);
err2 |= __put_user (high2lowgid(m.msg_perm.gid), &up->msg_perm.gid);
err2 |= __put_user (high2lowuid(m.msg_perm.cuid), &up->msg_perm.cuid);
err2 |= __put_user (high2lowgid(m.msg_perm.cgid), &up->msg_perm.cgid);
err2 |= __put_user (m.msg_perm.mode, &up->msg_perm.mode);
err2 |= __put_user (m.msg_perm.seq, &up->msg_perm.seq);
err2 |= __put_user (m.msg_stime, &up->msg_stime);
err2 |= __put_user (m.msg_rtime, &up->msg_rtime);
err2 |= __put_user (m.msg_ctime, &up->msg_ctime);
err2 |= __put_user (m.msg_cbytes, &up->msg_cbytes);
err2 |= __put_user (m.msg_qnum, &up->msg_qnum);
err2 |= __put_user (m.msg_qbytes, &up->msg_qbytes);
err2 |= __put_user (m.msg_lspid, &up->msg_lspid);
err2 |= __put_user (m.msg_lrpid, &up->msg_lrpid);
if (err2)
err = -EFAULT;
}
}
out:
return err;
}
static int do_sys32_shmat (int first, int second, int third, int version, void *uptr)
{
unsigned long raddr;
u32 *uaddr = (u32 *)A((u32)third);
int err = -EINVAL;
if (version == 1)
goto out;
err = do_shmat (first, uptr, second, &raddr);
if (err)
goto out;
err = put_user (raddr, uaddr);
out:
return err;
}
static int do_sys32_shmctl (int first, int second, void *uptr)
{
int err;
if (IPCOP_MASK (second) &
(IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SHM_LOCK) | IPCOP_MASK (SHM_UNLOCK) |
IPCOP_MASK (IPC_RMID))) {
if (second == (IPC_INFO|IPC_64))
second = IPC_INFO; /* So that we don't have to translate it */
err = sys_shmctl (first, second, (struct shmid_ds *)uptr);
} else if ((second & IPC_64) && second != (SHM_INFO|IPC_64)) {
struct shmid64_ds s;
struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr;
mm_segment_t old_fs;
if (second == (IPC_SET|IPC_64)) {
err = get_user (s.shm_perm.uid, &up->shm_perm.uid);
err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid);
err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode);
if (err)
goto out;
}
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_shmctl (first, second, (struct shmid_ds *)&s);
set_fs (old_fs);
if (err < 0)
goto out;
/* Mask it even in this case so it becomes a CSE. */
if (IPCOP_MASK (second) &
(IPCOP_MASK (SHM_STAT) | IPCOP_MASK (IPC_STAT))) {
int err2 = put_user (s.shm_perm.key, &up->shm_perm.key);
err2 |= __put_user (high2lowuid(s.shm_perm.uid), &up->shm_perm.uid);
err2 |= __put_user (high2lowgid(s.shm_perm.gid), &up->shm_perm.gid);
err2 |= __put_user (high2lowuid(s.shm_perm.cuid), &up->shm_perm.cuid);
err2 |= __put_user (high2lowgid(s.shm_perm.cgid), &up->shm_perm.cgid);
err2 |= __put_user (s.shm_perm.mode, &up->shm_perm.mode);
err2 |= __put_user (s.shm_perm.seq, &up->shm_perm.seq);
err2 |= __put_user (s.shm_atime, &up->shm_atime);
err2 |= __put_user (s.shm_dtime, &up->shm_dtime);
err2 |= __put_user (s.shm_ctime, &up->shm_ctime);
err2 |= __put_user (s.shm_segsz, &up->shm_segsz);
err2 |= __put_user (s.shm_nattch, &up->shm_nattch);
err2 |= __put_user (s.shm_cpid, &up->shm_cpid);
err2 |= __put_user (s.shm_lpid, &up->shm_lpid);
if (err2)
err = -EFAULT;
}
} else {
struct shmid_ds s;
struct shmid_ds32 *up = (struct shmid_ds32 *)uptr;
mm_segment_t old_fs;
second &= ~IPC_64;
if (second == IPC_SET) {
err = get_user (s.shm_perm.uid, &up->shm_perm.uid);
err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid);
err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode);
if (err)
goto out;
}
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_shmctl (first, second, &s);
set_fs (old_fs);
if (err < 0)
goto out;
/* Mask it even in this case so it becomes a CSE. */
if (second == SHM_INFO) {
struct shm_info32 {
int used_ids;
u32 shm_tot, shm_rss, shm_swp;
u32 swap_attempts, swap_successes;
} *uip = (struct shm_info32 *)uptr;
struct shm_info *kp = (struct shm_info *)&s;
int err2 = put_user (kp->used_ids, &uip->used_ids);
err2 |= __put_user (kp->shm_tot, &uip->shm_tot);
err2 |= __put_user (kp->shm_rss, &uip->shm_rss);
err2 |= __put_user (kp->shm_swp, &uip->shm_swp);
err2 |= __put_user (kp->swap_attempts, &uip->swap_attempts);
err2 |= __put_user (kp->swap_successes, &uip->swap_successes);
if (err2)
err = -EFAULT;
} else if (IPCOP_MASK (second) &
(IPCOP_MASK (SHM_STAT) | IPCOP_MASK (IPC_STAT))) {
int err2 = put_user (s.shm_perm.key, &up->shm_perm.key);
err2 |= __put_user (high2lowuid(s.shm_perm.uid), &up->shm_perm.uid);
err2 |= __put_user (high2lowgid(s.shm_perm.gid), &up->shm_perm.gid);
err2 |= __put_user (high2lowuid(s.shm_perm.cuid), &up->shm_perm.cuid);
err2 |= __put_user (high2lowgid(s.shm_perm.cgid), &up->shm_perm.cgid);
err2 |= __put_user (s.shm_perm.mode, &up->shm_perm.mode);
err2 |= __put_user (s.shm_perm.seq, &up->shm_perm.seq);
err2 |= __put_user (s.shm_atime, &up->shm_atime);
err2 |= __put_user (s.shm_dtime, &up->shm_dtime);
err2 |= __put_user (s.shm_ctime, &up->shm_ctime);
err2 |= __put_user (s.shm_segsz, &up->shm_segsz);
err2 |= __put_user (s.shm_nattch, &up->shm_nattch);
err2 |= __put_user (s.shm_cpid, &up->shm_cpid);
err2 |= __put_user (s.shm_lpid, &up->shm_lpid);
if (err2)
err = -EFAULT;
}
}
out:
return err;
}
/* /*
* sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation. * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.
* *
...@@ -835,84 +300,64 @@ static int do_sys32_shmctl (int first, int second, void *uptr) ...@@ -835,84 +300,64 @@ static int do_sys32_shmctl (int first, int second, void *uptr)
*/ */
asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr) asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr)
{ {
int version, err; if(call >> 16) /* hack for backward compatibility */
return -EINVAL;
version = call >> 16; /* hack for backward compatibility */
call &= 0xffff; call &= 0xffff;
if(version)
return -EINVAL;
if (call <= SEMTIMEDOP) if (call <= SEMTIMEDOP)
switch (call) { switch (call) {
case SEMTIMEDOP: case SEMTIMEDOP:
if (third) { if (third)
err = do_sys32_semtimedop(first, return compat_sys_semtimedop(first,
(struct sembuf *)AA(ptr), compat_ptr(ptr), second,
second, compat_ptr(third));
(struct compat_timespec *)
AA((u32)third));
goto out;
}
/* else fall through for normal semop() */ /* else fall through for normal semop() */
case SEMOP: case SEMOP:
/* struct sembuf is the same on 32 and 64bit :)) */ /* struct sembuf is the same on 32 and 64bit :)) */
err = sys_semtimedop (first, (struct sembuf *)AA(ptr), return sys_semtimedop (first, compat_ptr(ptr),
second, NULL); second, NULL);
goto out;
case SEMGET: case SEMGET:
err = sys_semget (first, second, third); return sys_semget (first, second, third);
goto out;
case SEMCTL: case SEMCTL:
err = do_sys32_semctl (first, second, third, (void *)AA(ptr)); return compat_sys_semctl (first, second, third,
goto out; compat_ptr(ptr));
default: default:
err = -EINVAL; return -EINVAL;
goto out;
}; };
if (call <= MSGCTL) if (call <= MSGCTL)
switch (call) { switch (call) {
case MSGSND: case MSGSND:
err = do_sys32_msgsnd (first, second, third, (void *)AA(ptr)); return compat_sys_msgsnd (first, second, third,
goto out; compat_ptr(ptr));
case MSGRCV: case MSGRCV:
err = do_sys32_msgrcv (first, second, 0, third, return compat_sys_msgrcv (first, second, 0, third,
version, (void *)AA(ptr)); 0, compat_ptr(ptr));
goto out;
case MSGGET: case MSGGET:
err = sys_msgget ((key_t) first, second); return sys_msgget ((key_t) first, second);
goto out;
case MSGCTL: case MSGCTL:
err = do_sys32_msgctl (first, second, (void *)AA(ptr)); return compat_sys_msgctl (first, second,
goto out; compat_ptr(ptr));
default: default:
err = -EINVAL; return -EINVAL;
goto out;
} }
if (call <= SHMCTL) if (call <= SHMCTL)
switch (call) { switch (call) {
case SHMAT: case SHMAT:
err = do_sys32_shmat (first, second, third, return compat_sys_shmat (first, second, third,
version, (void *)AA(ptr)); 0, compat_ptr(ptr));
goto out;
case SHMDT: case SHMDT:
err = sys_shmdt ((char *)AA(ptr)); return sys_shmdt(compat_ptr(ptr));
goto out;
case SHMGET: case SHMGET:
err = sys_shmget (first, second, third); return sys_shmget(first, second, third);
goto out;
case SHMCTL: case SHMCTL:
err = do_sys32_shmctl (first, second, (void *)AA(ptr)); return compat_sys_shmctl(first, second,
goto out; compat_ptr(ptr));
default: default:
err = -EINVAL; return -EINVAL;
goto out;
} }
err = -EINVAL; return -EINVAL;
out:
return err;
} }
asmlinkage int sys32_truncate64(const char * path, unsigned long high, unsigned long low) asmlinkage int sys32_truncate64(const char * path, unsigned long high, unsigned long low)
......
...@@ -387,6 +387,10 @@ config COMPAT ...@@ -387,6 +387,10 @@ config COMPAT
depends on IA32_EMULATION depends on IA32_EMULATION
default y default y
config SYSVIPC_COMPAT
bool
depends on COMPAT && SYSVIPC
default y
config UID16 config UID16
bool bool
......
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/spinlock.h>
#include <linux/fs.h> #include <linux/list.h>
#include <linux/file.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/time.h>
#include <linux/sem.h> #include <linux/sem.h>
#include <linux/msg.h> #include <linux/msg.h>
#include <linux/mm.h>
#include <linux/shm.h> #include <linux/shm.h>
#include <linux/slab.h>
#include <linux/ipc.h> #include <linux/ipc.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <asm/mman.h>
#include <asm/types.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include <asm/ipc.h>
#include <asm/ia32.h>
/*
* sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation..
*
* This is really horribly ugly.
*/
struct msgbuf32 {
s32 mtype;
char mtext[1];
};
struct ipc_perm32 {
int key;
compat_uid_t uid;
compat_gid_t gid;
compat_uid_t cuid;
compat_gid_t cgid;
unsigned short mode;
unsigned short seq;
};
struct ipc64_perm32 {
unsigned key;
compat_uid32_t uid;
compat_gid32_t gid;
compat_uid32_t cuid;
compat_gid32_t cgid;
unsigned short mode;
unsigned short __pad1;
unsigned short seq;
unsigned short __pad2;
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;
unsigned int __unused1;
compat_time_t sem_ctime;
unsigned int __unused2;
unsigned int sem_nsems;
unsigned int __unused3;
unsigned int __unused4;
};
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;
unsigned int __unused1;
compat_time_t shm_dtime;
unsigned int __unused2;
compat_time_t shm_ctime;
unsigned int __unused3;
compat_pid_t shm_cpid;
compat_pid_t shm_lpid;
unsigned int shm_nattch;
unsigned int __unused4;
unsigned int __unused5;
};
struct shminfo64_32 {
unsigned int shmmax;
unsigned int shmmin;
unsigned int shmmni;
unsigned int shmseg;
unsigned int shmall;
unsigned int __unused1;
unsigned int __unused2;
unsigned int __unused3;
unsigned int __unused4;
};
struct shm_info32 {
int used_ids;
u32 shm_tot, shm_rss, shm_swp;
u32 swap_attempts, swap_successes;
};
struct ipc_kludge {
u32 msgp;
s32 msgtyp;
};
#define A(__x) ((unsigned long)(__x))
#define AA(__x) ((unsigned long)(__x))
#define SEMOP 1
#define SEMGET 2
#define SEMCTL 3
#define TIMEDSEMOP 4
#define MSGSND 11
#define MSGRCV 12
#define MSGGET 13
#define MSGCTL 14
#define SHMAT 21
#define SHMDT 22
#define SHMGET 23
#define SHMCTL 24
#define IPCOP_MASK(__x) (1UL << (__x))
static int
ipc_parse_version32 (int *cmd)
{
if (*cmd & IPC_64) {
*cmd ^= IPC_64;
return IPC_64;
} else {
return IPC_OLD;
}
}
static int put_semid(void *user_semid, struct semid64_ds *s, int version)
{
int err2;
switch (version) {
case IPC_64: {
struct semid64_ds32 *usp64 = (struct semid64_ds32 *) user_semid;
if (!access_ok(VERIFY_WRITE, usp64, sizeof(*usp64))) {
err2 = -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);
break;
}
default: {
struct semid_ds32 *usp32 = (struct semid_ds32 *) user_semid;
if (!access_ok(VERIFY_WRITE, usp32, sizeof(*usp32))) {
err2 = -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);
break;
}
}
return err2;
}
static int
semctl32 (int first, int second, int third, void *uptr)
{
union semun fourth;
u32 pad;
int err;
struct semid64_ds s;
mm_segment_t old_fs;
int version = ipc_parse_version32(&third);
if (!uptr)
return -EINVAL;
if (get_user(pad, (u32 *)uptr))
return -EFAULT;
if (third == SETVAL)
fourth.val = (int)pad;
else
fourth.__pad = (void *)A(pad);
switch (third) {
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 = &s;
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_semctl(first, second, third, fourth);
set_fs(old_fs);
if (!err)
err = put_semid((void *)A(pad), &s, version);
break;
default:
err = -EINVAL;
break;
}
return err;
}
#define MAXBUF (64*1024)
static int
do_sys32_msgsnd (int first, int second, int third, void *uptr)
{
struct msgbuf *p;
struct msgbuf32 *up = (struct msgbuf32 *)uptr;
mm_segment_t old_fs;
int err;
if (second >= MAXBUF-sizeof(struct msgbuf))
return -EINVAL;
p = kmalloc(second + sizeof(struct msgbuf), GFP_USER);
if (!p)
return -ENOMEM;
err = get_user(p->mtype, &up->mtype);
err |= (copy_from_user(p->mtext, &up->mtext, second) ? -EFAULT : 0);
if (err)
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;
}
static int
do_sys32_msgrcv (int first, int second, int msgtyp, int third, int version, void *uptr)
{
struct msgbuf32 *up;
struct msgbuf *p;
mm_segment_t old_fs;
int err;
if (!version) {
struct ipc_kludge *uipck = (struct ipc_kludge *)uptr;
struct ipc_kludge ipck;
err = -EINVAL;
if (!uptr)
goto out;
err = -EFAULT;
if (copy_from_user(&ipck, uipck, sizeof(struct ipc_kludge)))
goto out;
uptr = (void *)A(ipck.msgp);
msgtyp = ipck.msgtyp;
}
if (second >= MAXBUF-sizeof(struct msgbuf))
return -EINVAL;
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 = (struct msgbuf32 *)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
msgctl32 (int first, int second, void *uptr)
{
int err = -EINVAL, err2;
struct msqid_ds m;
struct msqid64_ds m64;
struct msqid_ds32 *up32 = (struct msqid_ds32 *)uptr;
struct msqid64_ds32 *up64 = (struct msqid64_ds32 *)uptr;
mm_segment_t old_fs;
int version = ipc_parse_version32(&second);
switch (second) {
case IPC_INFO:
case IPC_RMID:
case MSG_INFO:
err = sys_msgctl(first, second, (struct msqid_ds *)uptr);
break;
case IPC_SET:
if (version == IPC_64) {
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 {
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, &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, (void *) &m64);
set_fs(old_fs);
if (version == IPC_64) {
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) {
err = -EFAULT;
break;
}
err2 = __put_user(m64.msg_perm.key, &up64->msg_perm.key);
err2 |= __put_user(m64.msg_perm.uid, &up64->msg_perm.uid);
err2 |= __put_user(m64.msg_perm.gid, &up64->msg_perm.gid);
err2 |= __put_user(m64.msg_perm.cuid, &up64->msg_perm.cuid);
err2 |= __put_user(m64.msg_perm.cgid, &up64->msg_perm.cgid);
err2 |= __put_user(m64.msg_perm.mode, &up64->msg_perm.mode);
err2 |= __put_user(m64.msg_perm.seq, &up64->msg_perm.seq);
err2 |= __put_user(m64.msg_stime, &up64->msg_stime);
err2 |= __put_user(m64.msg_rtime, &up64->msg_rtime);
err2 |= __put_user(m64.msg_ctime, &up64->msg_ctime);
err2 |= __put_user(m64.msg_cbytes, &up64->msg_cbytes);
err2 |= __put_user(m64.msg_qnum, &up64->msg_qnum);
err2 |= __put_user(m64.msg_qbytes, &up64->msg_qbytes);
err2 |= __put_user(m64.msg_lspid, &up64->msg_lspid);
err2 |= __put_user(m64.msg_lrpid, &up64->msg_lrpid);
if (err2)
err = -EFAULT;
} else {
if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) {
err = -EFAULT;
break;
}
err2 = __put_user(m64.msg_perm.key, &up32->msg_perm.key);
err2 |= __put_user(m64.msg_perm.uid, &up32->msg_perm.uid);
err2 |= __put_user(m64.msg_perm.gid, &up32->msg_perm.gid);
err2 |= __put_user(m64.msg_perm.cuid, &up32->msg_perm.cuid);
err2 |= __put_user(m64.msg_perm.cgid, &up32->msg_perm.cgid);
err2 |= __put_user(m64.msg_perm.mode, &up32->msg_perm.mode);
err2 |= __put_user(m64.msg_perm.seq, &up32->msg_perm.seq);
err2 |= __put_user(m64.msg_stime, &up32->msg_stime);
err2 |= __put_user(m64.msg_rtime, &up32->msg_rtime);
err2 |= __put_user(m64.msg_ctime, &up32->msg_ctime);
err2 |= __put_user(m64.msg_cbytes, &up32->msg_cbytes);
err2 |= __put_user(m64.msg_qnum, &up32->msg_qnum);
err2 |= __put_user(m64.msg_qbytes, &up32->msg_qbytes);
err2 |= __put_user(m64.msg_lspid, &up32->msg_lspid);
err2 |= __put_user(m64.msg_lrpid, &up32->msg_lrpid);
if (err2)
err = -EFAULT;
}
break;
}
return err;
}
static int
shmat32 (int first, int second, int third, int version, void *uptr)
{
unsigned long raddr;
u32 *uaddr = (u32 *)A((u32)third);
int err;
if (version == 1)
return -EINVAL; /* iBCS2 emulator entry point: unsupported */
err = do_shmat(first, uptr, second, &raddr);
if (err)
return err;
return put_user(raddr, uaddr);
}
static int put_shmid64(struct shmid64_ds *s64p, void *uptr, int version)
{
int err2;
#define s64 (*s64p)
if (version == IPC_64) {
struct shmid64_ds32 *up64 = (struct shmid64_ds32 *)uptr;
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
return -EFAULT;
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 {
struct shmid_ds32 *up32 = (struct shmid_ds32 *)uptr;
if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32)))
return -EFAULT;
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);
}
#undef s64
return err2 ? -EFAULT : 0;
}
static int
shmctl32 (int first, int second, void *uptr)
{
int err = -EFAULT, err2;
struct shmid_ds s;
struct shmid64_ds s64;
mm_segment_t old_fs;
struct shm_info32 *uip = (struct shm_info32 *)uptr;
struct shm_info si;
int version = ipc_parse_version32(&second);
struct shminfo64 smi;
struct shminfo *usi32 = (struct shminfo *) uptr;
struct shminfo64_32 *usi64 = (struct shminfo64_32 *) uptr;
switch (second) {
case IPC_INFO:
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_shmctl(first, second, (struct shmid_ds *)&smi);
set_fs(old_fs);
if (version == IPC_64) {
if (!access_ok(VERIFY_WRITE, usi64, sizeof(*usi64))) {
err = -EFAULT;
break;
}
err2 = __put_user(smi.shmmax, &usi64->shmmax);
err2 |= __put_user(smi.shmmin, &usi64->shmmin);
err2 |= __put_user(smi.shmmni, &usi64->shmmni);
err2 |= __put_user(smi.shmseg, &usi64->shmseg);
err2 |= __put_user(smi.shmall, &usi64->shmall);
} else {
if (!access_ok(VERIFY_WRITE, usi32, sizeof(*usi32))) {
err = -EFAULT;
break;
}
err2 = __put_user(smi.shmmax, &usi32->shmmax);
err2 |= __put_user(smi.shmmin, &usi32->shmmin);
err2 |= __put_user(smi.shmmni, &usi32->shmmni);
err2 |= __put_user(smi.shmseg, &usi32->shmseg);
err2 |= __put_user(smi.shmall, &usi32->shmall);
}
if (err2)
err = -EFAULT;
break;
case IPC_RMID:
case SHM_LOCK:
case SHM_UNLOCK:
err = sys_shmctl(first, second, (struct shmid_ds *)uptr);
break;
case IPC_SET:
if (version == IPC_64) {
struct shmid64_ds32 *up64 = (struct shmid64_ds32 *)uptr;
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 {
struct shmid_ds32 *up32 = (struct shmid_ds32 *)uptr;
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, &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, (void *) &s64);
set_fs(old_fs);
if (err < 0)
break;
err2 = put_shmid64(&s64, uptr, version);
if (err2)
err = err2;
break;
case SHM_INFO:
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_shmctl(first, second, (void *)&si);
set_fs(old_fs);
if (err < 0)
break;
if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip))) {
err = -EFAULT;
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;
}
extern int sem_ctls[];
static long semtimedop32(int semid, struct sembuf *sb,
unsigned nsops, struct compat_timespec *ts32)
{
struct timespec ts;
mm_segment_t oldfs = get_fs();
long ret;
if (nsops > sem_ctls[2])
return -E2BIG;
if (!access_ok(VERIFY_READ, sb, nsops * sizeof(struct sembuf)))
return -EFAULT;
if (ts32 && get_compat_timespec(&ts, ts32))
return -EFAULT;
set_fs(KERNEL_DS);
ret = sys_semtimedop(semid, sb, nsops, ts32 ? &ts : NULL);
set_fs(oldfs);
return ret;
}
#include <asm-i386/ipc.h>
asmlinkage long asmlinkage long
sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) sys32_ipc(u32 call, int first, int second, int third,
compat_uptr_t ptr, u32 fifth)
{ {
int version; int version;
...@@ -660,35 +23,35 @@ sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) ...@@ -660,35 +23,35 @@ sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
switch (call) { switch (call) {
case SEMOP: case SEMOP:
/* struct sembuf is the same on 32 and 64bit :)) */ /* struct sembuf is the same on 32 and 64bit :)) */
return sys_semtimedop(first, (struct sembuf *)AA(ptr), second, return sys_semtimedop(first, compat_ptr(ptr), second, NULL);
NULL); case SEMTIMEDOP:
case TIMEDSEMOP: return compat_sys_semtimedop(first, compat_ptr(ptr), second,
return semtimedop32(first, (struct sembuf *)AA(ptr), second, compat_ptr(fifth));
(struct compat_timespec *)AA(fifth));
case SEMGET: case SEMGET:
return sys_semget(first, second, third); return sys_semget(first, second, third);
case SEMCTL: case SEMCTL:
return semctl32(first, second, third, (void *)AA(ptr)); return compat_sys_semctl(first, second, third, compat_ptr(ptr));
case MSGSND: case MSGSND:
return do_sys32_msgsnd(first, second, third, (void *)AA(ptr)); return compat_sys_msgsnd(first, second, third, compat_ptr(ptr));
case MSGRCV: case MSGRCV:
return do_sys32_msgrcv(first, second, fifth, third, version, (void *)AA(ptr)); return compat_sys_msgrcv(first, second, fifth, third,
version, compat_ptr(ptr));
case MSGGET: case MSGGET:
return sys_msgget((key_t) first, second); return sys_msgget((key_t) first, second);
case MSGCTL: case MSGCTL:
return msgctl32(first, second, (void *)AA(ptr)); return compat_sys_msgctl(first, second, compat_ptr(ptr));
case SHMAT: case SHMAT:
return shmat32(first, second, third, version, (void *)AA(ptr)); return compat_sys_shmat(first, second, third, version,
compat_ptr(ptr));
break; break;
case SHMDT: case SHMDT:
return sys_shmdt((char *)AA(ptr)); return sys_shmdt(compat_ptr(ptr));
case SHMGET: case SHMGET:
return sys_shmget(first, second, third); return sys_shmget(first, second, third);
case SHMCTL: case SHMCTL:
return shmctl32(first, second, (void *)AA(ptr)); return compat_sys_shmctl(first, second, compat_ptr(ptr));
} }
return -ENOSYS; return -ENOSYS;
} }
...@@ -11,6 +11,7 @@ typedef u32 compat_size_t; ...@@ -11,6 +11,7 @@ typedef u32 compat_size_t;
typedef s32 compat_ssize_t; typedef s32 compat_ssize_t;
typedef s32 compat_time_t; typedef s32 compat_time_t;
typedef s32 compat_clock_t; typedef s32 compat_clock_t;
typedef s32 compat_key_t;
typedef s32 compat_pid_t; typedef s32 compat_pid_t;
typedef u16 compat_uid_t; typedef u16 compat_uid_t;
typedef u16 compat_gid_t; typedef u16 compat_gid_t;
...@@ -116,6 +117,64 @@ typedef u32 compat_sigset_word; ...@@ -116,6 +117,64 @@ typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_OFF_T_MAX 0x7fffffff
#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL
struct compat_ipc64_perm {
compat_key_t key;
compat_uid32_t uid;
compat_gid32_t gid;
compat_uid32_t cuid;
compat_gid32_t cgid;
unsigned short mode;
unsigned short __pad1;
unsigned short seq;
unsigned short __pad2;
compat_ulong_t unused1;
compat_ulong_t unused2;
};
struct compat_semid64_ds {
struct compat_ipc64_perm sem_perm;
compat_time_t sem_otime;
compat_ulong_t __unused1;
compat_time_t sem_ctime;
compat_ulong_t __unused2;
compat_ulong_t sem_nsems;
compat_ulong_t __unused3;
compat_ulong_t __unused4;
};
struct compat_msqid64_ds {
struct compat_ipc64_perm msg_perm;
compat_time_t msg_stime;
compat_ulong_t __unused1;
compat_time_t msg_rtime;
compat_ulong_t __unused2;
compat_time_t msg_ctime;
compat_ulong_t __unused3;
compat_ulong_t msg_cbytes;
compat_ulong_t msg_qnum;
compat_ulong_t msg_qbytes;
compat_pid_t msg_lspid;
compat_pid_t msg_lrpid;
compat_ulong_t __unused4;
compat_ulong_t __unused5;
};
struct compat_shmid64_ds {
struct compat_ipc64_perm shm_perm;
compat_size_t shm_segsz;
compat_time_t shm_atime;
compat_ulong_t __unused1;
compat_time_t shm_dtime;
compat_ulong_t __unused2;
compat_time_t shm_ctime;
compat_ulong_t __unused3;
compat_pid_t shm_cpid;
compat_pid_t shm_lpid;
compat_ulong_t shm_nattch;
compat_ulong_t __unused4;
compat_ulong_t __unused5;
};
/* /*
* A pointer passed in from user mode. This should not be used for syscall parameters, * A pointer passed in from user mode. This should not be used for syscall parameters,
* just declare them as pointers because the syscall entry code will have appropriately * just declare them as pointers because the syscall entry code will have appropriately
......
...@@ -15,6 +15,8 @@ typedef s32 compat_clock_t; ...@@ -15,6 +15,8 @@ typedef s32 compat_clock_t;
typedef s32 compat_pid_t; typedef s32 compat_pid_t;
typedef u16 compat_uid_t; typedef u16 compat_uid_t;
typedef u16 compat_gid_t; typedef u16 compat_gid_t;
typedef u32 compat_uid32_t;
typedef u32 compat_gid32_t;
typedef u16 compat_mode_t; typedef u16 compat_mode_t;
typedef u32 compat_ino_t; typedef u32 compat_ino_t;
typedef u16 compat_dev_t; typedef u16 compat_dev_t;
...@@ -25,6 +27,7 @@ typedef u16 compat_ipc_pid_t; ...@@ -25,6 +27,7 @@ typedef u16 compat_ipc_pid_t;
typedef s32 compat_daddr_t; typedef s32 compat_daddr_t;
typedef u32 compat_caddr_t; typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t; typedef __kernel_fsid_t compat_fsid_t;
typedef s32 compat_key_t;
typedef s32 compat_timer_t; typedef s32 compat_timer_t;
typedef s32 compat_int_t; typedef s32 compat_int_t;
...@@ -135,4 +138,61 @@ static inline void *compat_alloc_user_space(long len) ...@@ -135,4 +138,61 @@ static inline void *compat_alloc_user_space(long len)
return (void *) (stack - len); return (void *) (stack - len);
} }
struct compat_ipc64_perm {
compat_key_t key;
compat_uid32_t uid;
compat_gid32_t gid;
compat_uid32_t cuid;
compat_gid32_t cgid;
compat_mode_t mode;
unsigned short __pad1;
unsigned short seq;
unsigned short __pad2;
unsigned int __unused1;
unsigned int __unused2;
};
struct compat_semid64_ds {
struct compat_ipc64_perm sem_perm;
compat_time_t sem_otime;
compat_ulong_t __pad1;
compat_time_t sem_ctime;
compat_ulong_t __pad2;
compat_ulong_t sem_nsems;
compat_ulong_t __unused1;
compat_ulong_t __unused2;
};
struct compat_msqid64_ds {
struct compat_ipc64_perm msg_perm;
compat_time_t msg_stime;
compat_ulong_t __pad1;
compat_time_t msg_rtime;
compat_ulong_t __pad2;
compat_time_t msg_ctime;
compat_ulong_t __pad3;
compat_ulong_t msg_cbytes;
compat_ulong_t msg_qnum;
compat_ulong_t msg_qbytes;
compat_pid_t msg_lspid;
compat_pid_t msg_lrpid;
compat_ulong_t __unused1;
compat_ulong_t __unused2;
};
struct compat_shmid64_ds {
struct compat_ipc64_perm shm_perm;
compat_size_t shm_segsz;
compat_time_t shm_atime;
compat_ulong_t __pad1;
compat_time_t shm_dtime;
compat_ulong_t __pad2;
compat_time_t shm_ctime;
compat_ulong_t __pad3;
compat_pid_t shm_cpid;
compat_pid_t shm_lpid;
compat_ulong_t shm_nattch;
compat_ulong_t __unused1;
compat_ulong_t __unused2;
};
#endif /* _ASM_S390X_COMPAT_H */ #endif /* _ASM_S390X_COMPAT_H */
...@@ -29,6 +29,7 @@ typedef s32 compat_daddr_t; ...@@ -29,6 +29,7 @@ typedef s32 compat_daddr_t;
typedef u32 compat_caddr_t; typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t; typedef __kernel_fsid_t compat_fsid_t;
typedef u32 compat_timer_t; typedef u32 compat_timer_t;
typedef s32 compat_key_t;
typedef s32 compat_int_t; typedef s32 compat_int_t;
typedef s32 compat_long_t; typedef s32 compat_long_t;
...@@ -119,6 +120,64 @@ typedef u32 compat_sigset_word; ...@@ -119,6 +120,64 @@ typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_OFF_T_MAX 0x7fffffff
#define COMPAT_LOFF_T_MAX 0x7fffffffffffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffff
struct compat_ipc64_perm {
compat_key_t key;
compat_uid32_t uid;
compat_gid32_t gid;
compat_uid32_t cuid;
compat_gid32_t cgid;
unsigned short mode;
unsigned short __pad1;
unsigned short seq;
unsigned short __pad2;
compat_ulong_t unused1;
compat_ulong_t unused2;
};
struct compat_semid64_ds {
struct compat_ipc64_perm sem_perm;
compat_time_t sem_otime;
compat_ulong_t __unused1;
compat_time_t sem_ctime;
compat_ulong_t __unused2;
compat_ulong_t sem_nsems;
compat_ulong_t __unused3;
compat_ulong_t __unused4;
};
struct compat_msqid64_ds {
struct compat_ipc64_perm msg_perm;
compat_time_t msg_stime;
compat_ulong_t __unused1;
compat_time_t msg_rtime;
compat_ulong_t __unused2;
compat_time_t msg_ctime;
compat_ulong_t __unused3;
compat_ulong_t msg_cbytes;
compat_ulong_t msg_qnum;
compat_ulong_t msg_qbytes;
compat_pid_t msg_lspid;
compat_pid_t msg_lrpid;
compat_ulong_t __unused4;
compat_ulong_t __unused5;
};
struct compat_shmid64_ds {
struct compat_ipc64_perm shm_perm;
compat_size_t shm_segsz;
compat_time_t shm_atime;
compat_ulong_t __unused1;
compat_time_t shm_dtime;
compat_ulong_t __unused2;
compat_time_t shm_ctime;
compat_ulong_t __unused3;
compat_pid_t shm_cpid;
compat_pid_t shm_lpid;
compat_ulong_t shm_nattch;
compat_ulong_t __unused4;
compat_ulong_t __unused5;
};
/* /*
* A pointer passed in from user mode. This should not * A pointer passed in from user mode. This should not
* be used for syscall parameters, just declare them * be used for syscall parameters, just declare them
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/param.h> /* for HZ */ #include <linux/param.h> /* for HZ */
#include <linux/sem.h>
#include <asm/compat.h> #include <asm/compat.h>
#define compat_jiffies_to_clock_t(x) \ #define compat_jiffies_to_clock_t(x) \
...@@ -88,5 +90,15 @@ typedef union compat_sigval { ...@@ -88,5 +90,15 @@ typedef union compat_sigval {
compat_uptr_t sival_ptr; compat_uptr_t sival_ptr;
} compat_sigval_t; } compat_sigval_t;
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 /* CONFIG_COMPAT */
#endif /* _LINUX_COMPAT_H */ #endif /* _LINUX_COMPAT_H */
...@@ -4,4 +4,5 @@ ...@@ -4,4 +4,5 @@
obj-y := util.o obj-y := util.o
obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o
obj-$(CONFIG_SYSVIPC) += msg.o sem.o shm.o obj-$(CONFIG_SYSVIPC) += msg.o sem.o shm.o
/*
* 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 <arun.sharma@intel.com>
* Copyright (C) 2000 VA Linux Co
* Copyright (C) 2000 Don Dugger <n0ano@valinux.com>
* Copyright (C) 2000 Hewlett-Packard Co.
* Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
* 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 <linux/compat.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/highuid.h>
#include <linux/init.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#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_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;
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)
{
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;
}
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 ipc64_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 ipc64_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 semid64_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 semid64_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;
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:
fourth.__pad = &s64;
err = do_semctl(first, second, third, fourth);
if (err < 0)
break;
if (version == IPC_64) {
err2 = put_compat_semid64_ds(&s64, compat_ptr(pad));
} else {
err2 = put_compat_semid_ds(&s64, compat_ptr(pad));
}
if (err2)
err = -EFAULT;
break;
case IPC_SET:
if (version == IPC_64) {
err = get_compat_semid64_ds(&s64, compat_ptr(pad));
} else {
err = get_compat_semid_ds(&s64, compat_ptr(pad));
}
if (err)
break;
fourth.__pad = &s64;
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;
p = kmalloc(second + sizeof(struct msgbuf), GFP_USER);
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;
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 msqid64_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 msqid64_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 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);
} else {
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) {
err2 = put_compat_msqid64_ds(&m64, uptr);
} else {
err2 = put_compat_msqid_ds(&m64, 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 = do_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 shmid64_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 shmid64_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_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)
{
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 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_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);
} else {
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) {
err2 = put_compat_shmid64_ds(&s64, uptr);
} else {
err2 = put_compat_shmid_ds(&s64, 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment