Commit 87c22e84 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] compat emulation for posix message queues

From: Arnd Bergmann <arnd@arndb.de>

I have tested the code with the open posix test suite and found the same
four failures for both 64-bit and compat mode, most tests pass.  The patch
is against -mc1, but I guess it also applies to the other trees around.

What worries me more than mq_attr compatibility is the conversion of struct
sigevent, which might turn out really hard when more fields in there are
used.  AFAICS, the only other part in the kernel ABI is sys_timer_create(),
so maybe it's not too late to deprecate the current structure and create a
structure that can be used properly for compat syscalls.
parent 34b98f22
......@@ -114,7 +114,12 @@ copy_siginfo_from_user32 (siginfo_t *to, siginfo_t32 *from)
err |= __get_user(to->si_band, &from->si_band);
err |= __get_user(to->si_fd, &from->si_fd);
break;
/* case __SI_RT: This is not generated by the kernel as of now. */
case __SI_RT: /* This is not generated by the kernel as of now. */
case __SI_MESGQ:
err |= __get_user(to->si_pid, &from->si_pid);
err |= __get_user(to->si_uid, &from->si_uid);
err |= __get_user(to->si_int, &from->si_int);
break;
}
}
return err;
......
......@@ -358,7 +358,12 @@ static int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
err |= __put_user(from->si_band, &to->si_band);
err |= __put_user(from->si_fd, &to->si_fd);
break;
/* case __SI_RT: This is not generated by the kernel as of now. */
case __SI_RT: /* This is not generated by the kernel as of now. */
case __SI_MESGQ:
err |= __put_user(from->si_pid, &to->si_pid);
err |= __put_user(from->si_uid, &to->si_uid);
err |= __put_user(from->si_int, &to->si_int);
break;
}
}
return err;
......
......@@ -74,6 +74,10 @@ int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
else {
switch (from->si_code >> 16) {
case __SI_RT: /* This is not generated by the kernel as of now. */
case __SI_MESGQ:
err |= __put_user(from->si_int, &to->si_int);
/* fallthrough */
case __SI_KILL >> 16:
err |= __put_user(from->si_pid, &to->si_pid);
err |= __put_user(from->si_uid, &to->si_uid);
......@@ -96,7 +100,6 @@ int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
break;
default:
break;
/* case __SI_RT: This is not generated by the kernel as of now. */
}
}
return err;
......
......@@ -129,7 +129,12 @@ int copy_siginfo_to_user32(siginfo_t32 __user *to, siginfo_t *from)
err |= __put_user(from->si_trapno, &to->si_trapno);
err |= __put_user((long)from->si_addr, &to->si_addr);
break;
/* case __SI_RT: This is not generated by the kernel as of now. */
case __SI_RT: /* This is not generated by the kernel as of now. */
case __SI_MESGQ:
err |= __put_user(from->si_pid, &to->si_pid);
err |= __put_user(from->si_uid, &to->si_uid);
err |= __put_user(from->si_int, &to->si_int);
break;
}
}
return err;
......
......@@ -85,7 +85,11 @@ int ia32_copy_siginfo_to_user(siginfo_t32 __user *to, siginfo_t *from)
err |= __put_user(from->si_overrun, &to->si_overrun);
err |= __put_user((u32)(u64)from->si_ptr, &to->si_ptr);
break;
/* case __SI_RT: This is not generated by the kernel as of now. */
case __SI_RT: /* This is not generated by the kernel as of now. */
case __SI_MESGQ:
err |= __put_user(from->si_uid, &to->si_uid);
err |= __put_user(from->si_int, &to->si_int);
break;
}
}
return err;
......
......@@ -141,20 +141,6 @@ struct ucontext32 {
struct mcontext32 uc_mcontext;
};
typedef struct compat_sigevent {
compat_sigval_t sigev_value;
int sigev_signo;
int sigev_notify;
union {
int _pad[SIGEV_PAD_SIZE];
int _tid;
struct {
compat_uptr_t _function;
compat_uptr_t _attribute;
} _sigev_thread;
} _sigev_un;
} compat_sigevent_t;
struct ipc_kludge_32 {
unsigned int msgp;
int msgtyp;
......
......@@ -13,6 +13,7 @@
#include <linux/sem.h>
#include <asm/compat.h>
#include <asm/siginfo.h>
#define compat_jiffies_to_clock_t(x) \
(((unsigned long)(x) * COMPAT_USER_HZ) / HZ)
......@@ -90,6 +91,22 @@ typedef union compat_sigval {
compat_uptr_t sival_ptr;
} compat_sigval_t;
typedef struct compat_sigevent {
compat_sigval_t sigev_value;
compat_int_t sigev_signo;
compat_int_t sigev_notify;
union {
compat_int_t _pad[SIGEV_PAD_SIZE];
compat_int_t _tid;
struct {
compat_uptr_t _function;
compat_uptr_t _attribute;
} _sigev_thread;
} _sigev_un;
} compat_sigevent_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,
......
......@@ -18,9 +18,9 @@
#ifndef _LINUX_MQUEUE_H
#define _LINUX_MQUEUE_H
#define MQ_PRIO_MAX 32768
#include <linux/types.h>
typedef int mqd_t;
#define MQ_PRIO_MAX 32768
struct mq_attr {
long mq_flags; /* message queue flags */
......
......@@ -42,6 +42,7 @@ typedef void (*__kernel_sighandler_t)(int);
/* Type of a SYSV IPC key. */
typedef int __kernel_key_t;
typedef int __kernel_mqd_t;
#include <asm/posix_types.h>
......
......@@ -48,7 +48,6 @@ struct timex;
struct timezone;
struct tms;
struct utimbuf;
typedef int mqd_t;
struct mq_attr;
#include <linux/config.h>
......
......@@ -31,6 +31,7 @@ typedef __kernel_key_t key_t;
typedef __kernel_suseconds_t suseconds_t;
typedef __kernel_timer_t timer_t;
typedef __kernel_clockid_t clockid_t;
typedef __kernel_mqd_t mqd_t;
#ifdef __KERNEL__
typedef __kernel_uid32_t uid_t;
......
......@@ -4,5 +4,6 @@
obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o
obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o
obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o
obj_mq-$(CONFIG_COMPAT) += compat_mq.o
obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y)
/*
* ipc/compat_mq.c
* 32 bit emulation for POSIX message queue system calls
*
* Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author: Arnd Bergmann <arnd@arndb.de>
*/
#include <linux/compat.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/mqueue.h>
#include <linux/syscalls.h>
#include <asm/uaccess.h>
struct compat_mq_attr {
compat_long_t mq_flags; /* message queue flags */
compat_long_t mq_maxmsg; /* maximum number of messages */
compat_long_t mq_msgsize; /* maximum message size */
compat_long_t mq_curmsgs; /* number of messages currently queued */
compat_long_t __reserved[4]; /* ignored for input, zeroed for output */
};
static inline int get_compat_mq_attr(struct mq_attr *attr,
const struct compat_mq_attr __user *uattr)
{
if (verify_area(VERIFY_READ, uattr, sizeof *uattr))
return -EFAULT;
return __get_user(attr->mq_flags, &uattr->mq_flags)
| __get_user(attr->mq_maxmsg, &uattr->mq_maxmsg)
| __get_user(attr->mq_msgsize, &uattr->mq_msgsize)
| __get_user(attr->mq_curmsgs, &uattr->mq_curmsgs);
}
static inline int put_compat_mq_attr(const struct mq_attr *attr,
struct compat_mq_attr __user *uattr)
{
if (clear_user(uattr, sizeof *uattr))
return -EFAULT;
return __put_user(attr->mq_flags, &uattr->mq_flags)
| __put_user(attr->mq_maxmsg, &uattr->mq_maxmsg)
| __put_user(attr->mq_msgsize, &uattr->mq_msgsize)
| __put_user(attr->mq_curmsgs, &uattr->mq_curmsgs);
}
asmlinkage long compat_sys_mq_open(const char __user *u_name,
int oflag, compat_mode_t mode,
struct compat_mq_attr __user *u_attr)
{
struct mq_attr attr;
mm_segment_t oldfs;
char *name;
long ret;
if ((oflag & O_CREAT) == 0 || !u_attr)
return sys_mq_open(u_name, oflag, mode, 0);
if (get_compat_mq_attr(&attr, u_attr))
return -EFAULT;
name = getname(u_name);
if (IS_ERR(name))
return PTR_ERR(name);
oldfs = get_fs();
set_fs(KERNEL_DS);
ret = sys_mq_open(name, oflag, mode, &attr);
set_fs(oldfs);
putname(name);
return ret;
}
static struct timespec __user *compat_prepare_timeout(
const struct compat_timespec __user *u_abs_timeout)
{
struct timespec ts, __user *u_ts;
if (!u_abs_timeout)
return 0;
u_ts = compat_alloc_user_space(sizeof(*u_ts));
if (get_compat_timespec(&ts, u_abs_timeout)
|| copy_to_user(u_ts, &ts, sizeof(*u_ts)))
return ERR_PTR(-EFAULT);
return u_ts;
}
asmlinkage long compat_sys_mq_timedsend(mqd_t mqdes,
const char __user *u_msg_ptr,
size_t msg_len, unsigned int msg_prio,
const struct compat_timespec __user *u_abs_timeout)
{
struct timespec __user *u_ts;
u_ts = compat_prepare_timeout(u_abs_timeout);
if (IS_ERR(u_ts))
return -EFAULT;
return sys_mq_timedsend(mqdes, u_msg_ptr, msg_len,
msg_prio, u_ts);
}
asmlinkage ssize_t compat_sys_mq_timedreceive(mqd_t mqdes,
char __user *u_msg_ptr,
size_t msg_len, unsigned int __user *u_msg_prio,
const struct compat_timespec __user *u_abs_timeout)
{
struct timespec *u_ts;
u_ts = compat_prepare_timeout(u_abs_timeout);
if (IS_ERR(u_ts))
return -EFAULT;
return sys_mq_timedreceive(mqdes, u_msg_ptr, msg_len,
u_msg_prio, u_ts);
}
static int get_compat_sigevent(struct sigevent *event,
const struct compat_sigevent __user *u_event)
{
if (verify_area(VERIFY_READ, u_event, sizeof(*u_event)))
return -EFAULT;
return __get_user(event->sigev_value.sival_int,
&u_event->sigev_value.sival_int)
| __get_user(event->sigev_signo, &u_event->sigev_signo)
| __get_user(event->sigev_notify, &u_event->sigev_notify)
| __get_user(event->sigev_notify_thread_id,
&u_event->sigev_notify_thread_id);
}
asmlinkage long compat_sys_mq_notify(mqd_t mqdes,
const struct compat_sigevent __user *u_notification)
{
mm_segment_t oldfs;
struct sigevent notification;
char cookie[NOTIFY_COOKIE_LEN];
compat_uptr_t u_cookie;
long ret;
if (!u_notification)
return sys_mq_notify(mqdes, 0);
if (get_compat_sigevent(&notification, u_notification))
return -EFAULT;
if (notification.sigev_notify == SIGEV_THREAD) {
u_cookie = (compat_uptr_t)notification.sigev_value.sival_int;
if (copy_from_user(cookie, compat_ptr(u_cookie),
NOTIFY_COOKIE_LEN)) {
return -EFAULT;
}
notification.sigev_value.sival_ptr = cookie;
}
oldfs = get_fs();
set_fs(KERNEL_DS);
ret = sys_mq_notify(mqdes, &notification);
set_fs(oldfs);
return ret;
}
asmlinkage long compat_sys_mq_getsetattr(mqd_t mqdes,
const struct compat_mq_attr __user *u_mqstat,
struct compat_mq_attr __user *u_omqstat)
{
struct mq_attr mqstat, omqstat;
struct mq_attr *p_mqstat = 0, *p_omqstat = 0;
mm_segment_t oldfs;
long ret;
if (u_mqstat) {
p_mqstat = &mqstat;
if (get_compat_mq_attr(p_mqstat, u_mqstat))
return -EFAULT;
}
if (u_omqstat)
p_omqstat = &omqstat;
oldfs = get_fs();
set_fs(KERNEL_DS);
ret = sys_mq_getsetattr(mqdes, p_mqstat, p_omqstat);
set_fs(oldfs);
if (ret)
return ret;
return (u_omqstat) ? put_compat_mq_attr(&omqstat, u_omqstat) : 0;
}
......@@ -266,6 +266,11 @@ cond_syscall(sys_mq_timedsend)
cond_syscall(sys_mq_timedreceive)
cond_syscall(sys_mq_notify)
cond_syscall(sys_mq_getsetattr)
cond_syscall(compat_sys_mq_open)
cond_syscall(compat_sys_mq_timedsend)
cond_syscall(compat_sys_mq_timedreceive)
cond_syscall(compat_sys_mq_notify)
cond_syscall(compat_sys_mq_getsetattr)
/* arch-specific weak syscall entries */
cond_syscall(sys_pciconfig_read)
......
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