Merge branch 'work.ipc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull ipc compat cleanup and 64-bit time_t from Al Viro: "IPC copyin/copyout sanitizing, including 64bit time_t work from Deepa Dinamani" * 'work.ipc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: utimes: Make utimes y2038 safe ipc: shm: Make shmid_kernel timestamps y2038 safe ipc: sem: Make sem_array timestamps y2038 safe ipc: msg: Make msg_queue timestamps y2038 safe ipc: mqueue: Replace timespec with timespec64 ipc: Make sys_semtimedop() y2038 safe get rid of SYSVIPC_COMPAT on ia64 semtimedop(): move compat to native shmat(2): move compat to native msgrcv(2), msgsnd(2): move compat to native ipc(2): move compat to native ipc: make use of compat ipc_perm helpers semctl(): move compat to native semctl(): separate all layout-dependent copyin/copyout msgctl(): move compat to native msgctl(): split the actual work from copyin/copyout ipc: move compat shmctl to native shmctl: split the work from copyin/copyout
This commit is contained in:
commit
cc73fee0ba
|
@ -56,9 +56,4 @@ config IA64_DEBUG_IRQ
|
|||
and restore instructions. It's useful for tracking down spinlock
|
||||
problems, but slow! If you're unsure, select N.
|
||||
|
||||
config SYSVIPC_COMPAT
|
||||
bool
|
||||
depends on COMPAT && SYSVIPC
|
||||
default y
|
||||
|
||||
endmenu
|
||||
|
|
23
fs/utimes.c
23
fs/utimes.c
|
@ -22,7 +22,7 @@
|
|||
*/
|
||||
SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times)
|
||||
{
|
||||
struct timespec tv[2];
|
||||
struct timespec64 tv[2];
|
||||
|
||||
if (times) {
|
||||
if (get_user(tv[0].tv_sec, ×->actime) ||
|
||||
|
@ -44,7 +44,7 @@ static bool nsec_valid(long nsec)
|
|||
return nsec >= 0 && nsec <= 999999999;
|
||||
}
|
||||
|
||||
static int utimes_common(const struct path *path, struct timespec *times)
|
||||
static int utimes_common(const struct path *path, struct timespec64 *times)
|
||||
{
|
||||
int error;
|
||||
struct iattr newattrs;
|
||||
|
@ -115,7 +115,7 @@ out:
|
|||
* must be owner or have write permission.
|
||||
* Else, update from *times, must be owner or super user.
|
||||
*/
|
||||
long do_utimes(int dfd, const char __user *filename, struct timespec *times,
|
||||
long do_utimes(int dfd, const char __user *filename, struct timespec64 *times,
|
||||
int flags)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
|
@ -167,10 +167,11 @@ out:
|
|||
SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename,
|
||||
struct timespec __user *, utimes, int, flags)
|
||||
{
|
||||
struct timespec tstimes[2];
|
||||
struct timespec64 tstimes[2];
|
||||
|
||||
if (utimes) {
|
||||
if (copy_from_user(&tstimes, utimes, sizeof(tstimes)))
|
||||
if ((get_timespec64(&tstimes[0], &utimes[0]) ||
|
||||
get_timespec64(&tstimes[1], &utimes[1])))
|
||||
return -EFAULT;
|
||||
|
||||
/* Nothing to do, we must not even check the path. */
|
||||
|
@ -186,7 +187,7 @@ SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename,
|
|||
struct timeval __user *, utimes)
|
||||
{
|
||||
struct timeval times[2];
|
||||
struct timespec tstimes[2];
|
||||
struct timespec64 tstimes[2];
|
||||
|
||||
if (utimes) {
|
||||
if (copy_from_user(×, utimes, sizeof(times)))
|
||||
|
@ -224,7 +225,7 @@ SYSCALL_DEFINE2(utimes, char __user *, filename,
|
|||
COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename,
|
||||
struct compat_utimbuf __user *, t)
|
||||
{
|
||||
struct timespec tv[2];
|
||||
struct timespec64 tv[2];
|
||||
|
||||
if (t) {
|
||||
if (get_user(tv[0].tv_sec, &t->actime) ||
|
||||
|
@ -238,11 +239,11 @@ COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename,
|
|||
|
||||
COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, struct compat_timespec __user *, t, int, flags)
|
||||
{
|
||||
struct timespec tv[2];
|
||||
struct timespec64 tv[2];
|
||||
|
||||
if (t) {
|
||||
if (compat_get_timespec(&tv[0], &t[0]) ||
|
||||
compat_get_timespec(&tv[1], &t[1]))
|
||||
if (compat_get_timespec64(&tv[0], &t[0]) ||
|
||||
compat_get_timespec64(&tv[1], &t[1]))
|
||||
return -EFAULT;
|
||||
|
||||
if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT)
|
||||
|
@ -253,7 +254,7 @@ COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filena
|
|||
|
||||
COMPAT_SYSCALL_DEFINE3(futimesat, unsigned int, dfd, const char __user *, filename, struct compat_timeval __user *, t)
|
||||
{
|
||||
struct timespec tv[2];
|
||||
struct timespec64 tv[2];
|
||||
|
||||
if (t) {
|
||||
if (get_user(tv[0].tv_sec, &t[0].tv_sec) ||
|
||||
|
|
|
@ -351,7 +351,7 @@ extern int __audit_socketcall(int nargs, unsigned long *args);
|
|||
extern int __audit_sockaddr(int len, void *addr);
|
||||
extern void __audit_fd_pair(int fd1, int fd2);
|
||||
extern void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr);
|
||||
extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
|
||||
extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec64 *abs_timeout);
|
||||
extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification);
|
||||
extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
|
||||
extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
|
||||
|
@ -412,7 +412,7 @@ static inline void audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr)
|
|||
if (unlikely(!audit_dummy_context()))
|
||||
__audit_mq_open(oflag, mode, attr);
|
||||
}
|
||||
static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout)
|
||||
static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec64 *abs_timeout)
|
||||
{
|
||||
if (unlikely(!audit_dummy_context()))
|
||||
__audit_mq_sendrecv(mqdes, msg_len, msg_prio, abs_timeout);
|
||||
|
@ -549,7 +549,7 @@ static inline void audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr)
|
|||
{ }
|
||||
static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len,
|
||||
unsigned int msg_prio,
|
||||
const struct timespec *abs_timeout)
|
||||
const struct timespec64 *abs_timeout)
|
||||
{ }
|
||||
static inline void audit_mq_notify(mqd_t mqdes,
|
||||
const struct sigevent *notification)
|
||||
|
|
|
@ -171,15 +171,6 @@ extern int get_compat_itimerspec64(struct itimerspec64 *its,
|
|||
extern int put_compat_itimerspec64(const struct itimerspec64 *its,
|
||||
struct compat_itimerspec __user *uits);
|
||||
|
||||
/*
|
||||
* This function convert a timespec if necessary and returns a *user
|
||||
* space* pointer. If no conversion is necessary, it returns the
|
||||
* initial pointer. NULL is a legitimate argument and will always
|
||||
* output NULL.
|
||||
*/
|
||||
extern int compat_convert_timespec(struct timespec __user **,
|
||||
const void __user *);
|
||||
|
||||
struct compat_iovec {
|
||||
compat_uptr_t iov_base;
|
||||
compat_size_t iov_len;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define _LINUX_MSG_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/time64.h>
|
||||
#include <uapi/linux/msg.h>
|
||||
|
||||
/* one msg_msg structure for each message */
|
||||
|
@ -17,9 +18,9 @@ struct msg_msg {
|
|||
/* one msq_queue structure for each present queue on the system */
|
||||
struct msg_queue {
|
||||
struct kern_ipc_perm q_perm;
|
||||
time_t q_stime; /* last msgsnd time */
|
||||
time_t q_rtime; /* last msgrcv time */
|
||||
time_t q_ctime; /* last change time */
|
||||
time64_t q_stime; /* last msgsnd time */
|
||||
time64_t q_rtime; /* last msgrcv time */
|
||||
time64_t q_ctime; /* last change time */
|
||||
unsigned long q_cbytes; /* current number of bytes on queue */
|
||||
unsigned long q_qnum; /* number of messages in queue */
|
||||
unsigned long q_qbytes; /* max number of bytes on queue */
|
||||
|
@ -31,12 +32,4 @@ struct msg_queue {
|
|||
struct list_head q_senders;
|
||||
} __randomize_layout;
|
||||
|
||||
/* Helper routines for sys_msgsnd and sys_msgrcv */
|
||||
extern long do_msgsnd(int msqid, long mtype, void __user *mtext,
|
||||
size_t msgsz, int msgflg);
|
||||
extern long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
|
||||
int msgflg,
|
||||
long (*msg_fill)(void __user *, struct msg_msg *,
|
||||
size_t));
|
||||
|
||||
#endif /* _LINUX_MSG_H */
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <linux/atomic.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/time64.h>
|
||||
#include <uapi/linux/sem.h>
|
||||
|
||||
struct task_struct;
|
||||
|
@ -30,7 +31,7 @@ struct sem {
|
|||
/* One sem_array data structure for each set of semaphores in the system. */
|
||||
struct sem_array {
|
||||
struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */
|
||||
time_t sem_ctime; /* create/last semctl() time */
|
||||
time64_t sem_ctime; /* create/last semctl() time */
|
||||
struct list_head pending_alter; /* pending operations */
|
||||
/* that alter the array */
|
||||
struct list_head pending_const; /* pending complex operations */
|
||||
|
|
|
@ -12,9 +12,9 @@ struct shmid_kernel /* private to the kernel */
|
|||
struct file *shm_file;
|
||||
unsigned long shm_nattch;
|
||||
unsigned long shm_segsz;
|
||||
time_t shm_atim;
|
||||
time_t shm_dtim;
|
||||
time_t shm_ctim;
|
||||
time64_t shm_atim;
|
||||
time64_t shm_dtim;
|
||||
time64_t shm_ctim;
|
||||
pid_t shm_cprid;
|
||||
pid_t shm_lprid;
|
||||
struct user_struct *mlock_user;
|
||||
|
|
|
@ -178,7 +178,7 @@ extern int do_setitimer(int which, struct itimerval *value,
|
|||
struct itimerval *ovalue);
|
||||
extern int do_getitimer(int which, struct itimerval *value);
|
||||
|
||||
extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags);
|
||||
extern long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, int flags);
|
||||
|
||||
/*
|
||||
* Similar to the struct tm in userspace <time.h>, but it needs to be here so
|
||||
|
|
|
@ -110,7 +110,7 @@ static void __init free_hash(void)
|
|||
|
||||
static long __init do_utime(char *filename, time_t mtime)
|
||||
{
|
||||
struct timespec t[2];
|
||||
struct timespec64 t[2];
|
||||
|
||||
t[0].tv_sec = mtime;
|
||||
t[0].tv_nsec = 0;
|
||||
|
|
740
ipc/compat.c
740
ipc/compat.c
|
@ -34,724 +34,48 @@
|
|||
|
||||
#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;
|
||||
};
|
||||
|
||||
static inline int compat_ipc_parse_version(int *cmd)
|
||||
int get_compat_ipc64_perm(struct ipc64_perm *to,
|
||||
struct compat_ipc64_perm __user *from)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION
|
||||
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;
|
||||
#else
|
||||
/* With the asm-generic APIs, we always use the 64-bit versions. */
|
||||
return IPC_64;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64,
|
||||
struct compat_ipc64_perm __user *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 __user *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 __user *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 __user *uip)
|
||||
{
|
||||
int err;
|
||||
__compat_uid_t u;
|
||||
__compat_gid_t g;
|
||||
|
||||
err = __put_user(p->key, &uip->key);
|
||||
SET_UID(u, p->uid);
|
||||
err |= __put_user(u, &uip->uid);
|
||||
SET_GID(g, p->gid);
|
||||
err |= __put_user(g, &uip->gid);
|
||||
SET_UID(u, p->cuid);
|
||||
err |= __put_user(u, &uip->cuid);
|
||||
SET_GID(g, p->cgid);
|
||||
err |= __put_user(g, &uip->cgid);
|
||||
err |= __put_user(p->mode, &uip->mode);
|
||||
err |= __put_user(p->seq, &uip->seq);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int get_compat_semid64_ds(struct semid64_ds *sem64,
|
||||
struct compat_semid64_ds __user *up64)
|
||||
{
|
||||
if (!access_ok(VERIFY_READ, up64, sizeof(*up64)))
|
||||
struct compat_ipc64_perm v;
|
||||
if (copy_from_user(&v, from, sizeof(v)))
|
||||
return -EFAULT;
|
||||
return __get_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm);
|
||||
to->uid = v.uid;
|
||||
to->gid = v.gid;
|
||||
to->mode = v.mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int get_compat_semid_ds(struct semid64_ds *s,
|
||||
struct compat_semid_ds __user *up)
|
||||
int get_compat_ipc_perm(struct ipc64_perm *to,
|
||||
struct compat_ipc_perm __user *from)
|
||||
{
|
||||
if (!access_ok(VERIFY_READ, up, sizeof(*up)))
|
||||
struct compat_ipc_perm v;
|
||||
if (copy_from_user(&v, from, sizeof(v)))
|
||||
return -EFAULT;
|
||||
return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm);
|
||||
to->uid = v.uid;
|
||||
to->gid = v.gid;
|
||||
to->mode = v.mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int put_compat_semid64_ds(struct semid64_ds *sem64,
|
||||
struct compat_semid64_ds __user *up64)
|
||||
void to_compat_ipc64_perm(struct compat_ipc64_perm *to, struct ipc64_perm *from)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
|
||||
return -EFAULT;
|
||||
err = __put_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm);
|
||||
err |= __put_user(sem64->sem_otime, &up64->sem_otime);
|
||||
err |= __put_user(sem64->sem_ctime, &up64->sem_ctime);
|
||||
err |= __put_user(sem64->sem_nsems, &up64->sem_nsems);
|
||||
return err;
|
||||
to->key = from->key;
|
||||
to->uid = from->uid;
|
||||
to->gid = from->gid;
|
||||
to->cuid = from->cuid;
|
||||
to->cgid = from->cgid;
|
||||
to->mode = from->mode;
|
||||
to->seq = from->seq;
|
||||
}
|
||||
|
||||
static inline int put_compat_semid_ds(struct semid64_ds *s,
|
||||
struct compat_semid_ds __user *up)
|
||||
void to_compat_ipc_perm(struct compat_ipc_perm *to, struct ipc64_perm *from)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
|
||||
return -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 long do_compat_semctl(int first, int second, int third, u32 pad)
|
||||
{
|
||||
unsigned long fourth;
|
||||
int err, err2;
|
||||
struct semid64_ds sem64;
|
||||
struct semid64_ds __user *up64;
|
||||
int version = compat_ipc_parse_version(&third);
|
||||
|
||||
memset(&sem64, 0, sizeof(sem64));
|
||||
|
||||
if ((third & (~IPC_64)) == SETVAL)
|
||||
#ifdef __BIG_ENDIAN
|
||||
fourth = (unsigned long)pad << 32;
|
||||
#else
|
||||
fourth = pad;
|
||||
#endif
|
||||
else
|
||||
fourth = (unsigned long)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:
|
||||
up64 = compat_alloc_user_space(sizeof(sem64));
|
||||
fourth = (unsigned long)up64;
|
||||
err = sys_semctl(first, second, third, fourth);
|
||||
if (err < 0)
|
||||
break;
|
||||
if (copy_from_user(&sem64, up64, sizeof(sem64)))
|
||||
err2 = -EFAULT;
|
||||
else if (version == IPC_64)
|
||||
err2 = put_compat_semid64_ds(&sem64, compat_ptr(pad));
|
||||
else
|
||||
err2 = put_compat_semid_ds(&sem64, compat_ptr(pad));
|
||||
if (err2)
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case IPC_SET:
|
||||
if (version == IPC_64)
|
||||
err = get_compat_semid64_ds(&sem64, compat_ptr(pad));
|
||||
else
|
||||
err = get_compat_semid_ds(&sem64, compat_ptr(pad));
|
||||
|
||||
up64 = compat_alloc_user_space(sizeof(sem64));
|
||||
if (copy_to_user(up64, &sem64, sizeof(sem64)))
|
||||
err = -EFAULT;
|
||||
if (err)
|
||||
break;
|
||||
|
||||
fourth = (unsigned long)up64;
|
||||
err = sys_semctl(first, second, third, fourth);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
|
||||
{
|
||||
struct compat_msgbuf __user *msgp = dest;
|
||||
size_t msgsz;
|
||||
|
||||
if (put_user(msg->m_type, &msgp->mtype))
|
||||
return -EFAULT;
|
||||
|
||||
msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz;
|
||||
if (store_msg(msgp->mtext, msg, msgsz))
|
||||
return -EFAULT;
|
||||
return msgsz;
|
||||
}
|
||||
|
||||
#ifndef COMPAT_SHMLBA
|
||||
#define COMPAT_SHMLBA SHMLBA
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
|
||||
COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second,
|
||||
u32, third, compat_uptr_t, ptr, u32, fifth)
|
||||
{
|
||||
int version;
|
||||
u32 pad;
|
||||
|
||||
version = call >> 16; /* hack for backward compatibility */
|
||||
call &= 0xffff;
|
||||
|
||||
switch (call) {
|
||||
case SEMOP:
|
||||
/* struct sembuf is the same on 32 and 64bit :)) */
|
||||
return sys_semtimedop(first, compat_ptr(ptr), second, NULL);
|
||||
case SEMTIMEDOP:
|
||||
return compat_sys_semtimedop(first, compat_ptr(ptr), second,
|
||||
compat_ptr(fifth));
|
||||
case SEMGET:
|
||||
return sys_semget(first, second, third);
|
||||
case SEMCTL:
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
if (get_user(pad, (u32 __user *) compat_ptr(ptr)))
|
||||
return -EFAULT;
|
||||
return do_compat_semctl(first, second, third, pad);
|
||||
|
||||
case MSGSND: {
|
||||
struct compat_msgbuf __user *up = compat_ptr(ptr);
|
||||
compat_long_t type;
|
||||
|
||||
if (first < 0 || second < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (get_user(type, &up->mtype))
|
||||
return -EFAULT;
|
||||
|
||||
return do_msgsnd(first, type, up->mtext, second, third);
|
||||
}
|
||||
case MSGRCV: {
|
||||
void __user *uptr = compat_ptr(ptr);
|
||||
|
||||
if (first < 0 || second < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!version) {
|
||||
struct compat_ipc_kludge ipck;
|
||||
if (!uptr)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ipck, uptr, sizeof(ipck)))
|
||||
return -EFAULT;
|
||||
uptr = compat_ptr(ipck.msgp);
|
||||
fifth = ipck.msgtyp;
|
||||
}
|
||||
return do_msgrcv(first, uptr, second, (s32)fifth, third,
|
||||
compat_do_msg_fill);
|
||||
}
|
||||
case MSGGET:
|
||||
return sys_msgget(first, second);
|
||||
case MSGCTL:
|
||||
return compat_sys_msgctl(first, second, compat_ptr(ptr));
|
||||
|
||||
case SHMAT: {
|
||||
int err;
|
||||
unsigned long raddr;
|
||||
|
||||
if (version == 1)
|
||||
return -EINVAL;
|
||||
err = do_shmat(first, compat_ptr(ptr), second, &raddr,
|
||||
COMPAT_SHMLBA);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return put_user(raddr, (compat_ulong_t *)compat_ptr(third));
|
||||
}
|
||||
case SHMDT:
|
||||
return sys_shmdt(compat_ptr(ptr));
|
||||
case SHMGET:
|
||||
return sys_shmget(first, (unsigned)second, third);
|
||||
case SHMCTL:
|
||||
return compat_sys_shmctl(first, second, compat_ptr(ptr));
|
||||
}
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg)
|
||||
{
|
||||
return do_compat_semctl(semid, semnum, cmd, arg);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp,
|
||||
compat_ssize_t, msgsz, int, msgflg)
|
||||
{
|
||||
struct compat_msgbuf __user *up = compat_ptr(msgp);
|
||||
compat_long_t mtype;
|
||||
|
||||
if (get_user(mtype, &up->mtype))
|
||||
return -EFAULT;
|
||||
return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp,
|
||||
compat_ssize_t, msgsz, compat_long_t, msgtyp, int, msgflg)
|
||||
{
|
||||
return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp,
|
||||
msgflg, compat_do_msg_fill);
|
||||
}
|
||||
|
||||
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 *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;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(msgctl, int, first, int, second, void __user *, uptr)
|
||||
{
|
||||
int err, err2;
|
||||
struct msqid64_ds m64;
|
||||
int version = compat_ipc_parse_version(&second);
|
||||
void __user *p;
|
||||
|
||||
memset(&m64, 0, sizeof(m64));
|
||||
|
||||
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;
|
||||
p = compat_alloc_user_space(sizeof(m64));
|
||||
if (copy_to_user(p, &m64, sizeof(m64)))
|
||||
err = -EFAULT;
|
||||
else
|
||||
err = sys_msgctl(first, second, p);
|
||||
break;
|
||||
|
||||
case IPC_STAT:
|
||||
case MSG_STAT:
|
||||
p = compat_alloc_user_space(sizeof(m64));
|
||||
err = sys_msgctl(first, second, p);
|
||||
if (err < 0)
|
||||
break;
|
||||
if (copy_from_user(&m64, p, sizeof(m64)))
|
||||
err2 = -EFAULT;
|
||||
else 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;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg)
|
||||
{
|
||||
unsigned long ret;
|
||||
long err;
|
||||
|
||||
err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA);
|
||||
if (err)
|
||||
return err;
|
||||
force_successful_syscall_return();
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
static inline int get_compat_shmid64_ds(struct shmid64_ds *sem64,
|
||||
struct compat_shmid64_ds __user *up64)
|
||||
{
|
||||
if (!access_ok(VERIFY_READ, up64, sizeof(*up64)))
|
||||
return -EFAULT;
|
||||
return __get_compat_ipc64_perm(&sem64->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 *sem64,
|
||||
struct compat_shmid64_ds __user *up64)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
|
||||
return -EFAULT;
|
||||
err = __put_compat_ipc64_perm(&sem64->shm_perm, &up64->shm_perm);
|
||||
err |= __put_user(sem64->shm_atime, &up64->shm_atime);
|
||||
err |= __put_user(sem64->shm_dtime, &up64->shm_dtime);
|
||||
err |= __put_user(sem64->shm_ctime, &up64->shm_ctime);
|
||||
err |= __put_user(sem64->shm_segsz, &up64->shm_segsz);
|
||||
err |= __put_user(sem64->shm_nattch, &up64->shm_nattch);
|
||||
err |= __put_user(sem64->shm_cpid, &up64->shm_cpid);
|
||||
err |= __put_user(sem64->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;
|
||||
if (smi->shmmax > INT_MAX)
|
||||
smi->shmmax = INT_MAX;
|
||||
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;
|
||||
if (smi->shmmax > INT_MAX)
|
||||
smi->shmmax = INT_MAX;
|
||||
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);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int put_compat_shm_info(struct shm_info __user *ip,
|
||||
struct compat_shm_info __user *uip)
|
||||
{
|
||||
int err;
|
||||
struct shm_info si;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip)) ||
|
||||
copy_from_user(&si, ip, sizeof(si)))
|
||||
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;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(shmctl, int, first, int, second, void __user *, uptr)
|
||||
{
|
||||
void __user *p;
|
||||
struct shmid64_ds sem64;
|
||||
struct shminfo64 smi;
|
||||
int err, err2;
|
||||
int version = compat_ipc_parse_version(&second);
|
||||
|
||||
memset(&sem64, 0, sizeof(sem64));
|
||||
|
||||
switch (second & (~IPC_64)) {
|
||||
case IPC_RMID:
|
||||
case SHM_LOCK:
|
||||
case SHM_UNLOCK:
|
||||
err = sys_shmctl(first, second, uptr);
|
||||
break;
|
||||
|
||||
case IPC_INFO:
|
||||
p = compat_alloc_user_space(sizeof(smi));
|
||||
err = sys_shmctl(first, second, p);
|
||||
if (err < 0)
|
||||
break;
|
||||
if (copy_from_user(&smi, p, sizeof(smi)))
|
||||
err2 = -EFAULT;
|
||||
else 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(&sem64, uptr);
|
||||
else
|
||||
err = get_compat_shmid_ds(&sem64, uptr);
|
||||
|
||||
if (err)
|
||||
break;
|
||||
p = compat_alloc_user_space(sizeof(sem64));
|
||||
if (copy_to_user(p, &sem64, sizeof(sem64)))
|
||||
err = -EFAULT;
|
||||
else
|
||||
err = sys_shmctl(first, second, p);
|
||||
break;
|
||||
|
||||
case IPC_STAT:
|
||||
case SHM_STAT:
|
||||
p = compat_alloc_user_space(sizeof(sem64));
|
||||
err = sys_shmctl(first, second, p);
|
||||
if (err < 0)
|
||||
break;
|
||||
if (copy_from_user(&sem64, p, sizeof(sem64)))
|
||||
err2 = -EFAULT;
|
||||
else if (version == IPC_64)
|
||||
err2 = put_compat_shmid64_ds(&sem64, uptr);
|
||||
else
|
||||
err2 = put_compat_shmid_ds(&sem64, uptr);
|
||||
if (err2)
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case SHM_INFO:
|
||||
p = compat_alloc_user_space(sizeof(struct shm_info));
|
||||
err = sys_shmctl(first, second, p);
|
||||
if (err < 0)
|
||||
break;
|
||||
err2 = put_compat_shm_info(p, uptr);
|
||||
if (err2)
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems,
|
||||
unsigned, nsops,
|
||||
const struct compat_timespec __user *, timeout)
|
||||
{
|
||||
struct timespec __user *ts64;
|
||||
if (compat_convert_timespec(&ts64, timeout))
|
||||
return -EFAULT;
|
||||
return sys_semtimedop(semid, tsems, nsops, ts64);
|
||||
to->key = from->key;
|
||||
SET_UID(to->uid, from->uid);
|
||||
SET_GID(to->gid, from->gid);
|
||||
SET_UID(to->cuid, from->cuid);
|
||||
SET_GID(to->cgid, from->cgid);
|
||||
to->mode = from->mode;
|
||||
to->seq = from->seq;
|
||||
}
|
||||
|
|
28
ipc/mqueue.c
28
ipc/mqueue.c
|
@ -668,11 +668,11 @@ static void __do_notify(struct mqueue_inode_info *info)
|
|||
}
|
||||
|
||||
static int prepare_timeout(const struct timespec __user *u_abs_timeout,
|
||||
struct timespec *ts)
|
||||
struct timespec64 *ts)
|
||||
{
|
||||
if (copy_from_user(ts, u_abs_timeout, sizeof(struct timespec)))
|
||||
if (get_timespec64(ts, u_abs_timeout))
|
||||
return -EFAULT;
|
||||
if (!timespec_valid(ts))
|
||||
if (!timespec64_valid(ts))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
@ -962,7 +962,7 @@ static inline void pipelined_receive(struct wake_q_head *wake_q,
|
|||
|
||||
static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
|
||||
size_t msg_len, unsigned int msg_prio,
|
||||
struct timespec *ts)
|
||||
struct timespec64 *ts)
|
||||
{
|
||||
struct fd f;
|
||||
struct inode *inode;
|
||||
|
@ -979,7 +979,7 @@ static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
|
|||
return -EINVAL;
|
||||
|
||||
if (ts) {
|
||||
expires = timespec_to_ktime(*ts);
|
||||
expires = timespec64_to_ktime(*ts);
|
||||
timeout = &expires;
|
||||
}
|
||||
|
||||
|
@ -1080,7 +1080,7 @@ out:
|
|||
|
||||
static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
|
||||
size_t msg_len, unsigned int __user *u_msg_prio,
|
||||
struct timespec *ts)
|
||||
struct timespec64 *ts)
|
||||
{
|
||||
ssize_t ret;
|
||||
struct msg_msg *msg_ptr;
|
||||
|
@ -1092,7 +1092,7 @@ static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
|
|||
struct posix_msg_tree_node *new_leaf = NULL;
|
||||
|
||||
if (ts) {
|
||||
expires = timespec_to_ktime(*ts);
|
||||
expires = timespec64_to_ktime(*ts);
|
||||
timeout = &expires;
|
||||
}
|
||||
|
||||
|
@ -1184,7 +1184,7 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
|
|||
size_t, msg_len, unsigned int, msg_prio,
|
||||
const struct timespec __user *, u_abs_timeout)
|
||||
{
|
||||
struct timespec ts, *p = NULL;
|
||||
struct timespec64 ts, *p = NULL;
|
||||
if (u_abs_timeout) {
|
||||
int res = prepare_timeout(u_abs_timeout, &ts);
|
||||
if (res)
|
||||
|
@ -1198,7 +1198,7 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
|
|||
size_t, msg_len, unsigned int __user *, u_msg_prio,
|
||||
const struct timespec __user *, u_abs_timeout)
|
||||
{
|
||||
struct timespec ts, *p = NULL;
|
||||
struct timespec64 ts, *p = NULL;
|
||||
if (u_abs_timeout) {
|
||||
int res = prepare_timeout(u_abs_timeout, &ts);
|
||||
if (res)
|
||||
|
@ -1475,11 +1475,11 @@ COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name,
|
|||
}
|
||||
|
||||
static int compat_prepare_timeout(const struct compat_timespec __user *p,
|
||||
struct timespec *ts)
|
||||
struct timespec64 *ts)
|
||||
{
|
||||
if (compat_get_timespec(ts, p))
|
||||
if (compat_get_timespec64(ts, p))
|
||||
return -EFAULT;
|
||||
if (!timespec_valid(ts))
|
||||
if (!timespec64_valid(ts))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1489,7 +1489,7 @@ COMPAT_SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes,
|
|||
compat_size_t, msg_len, unsigned int, msg_prio,
|
||||
const struct compat_timespec __user *, u_abs_timeout)
|
||||
{
|
||||
struct timespec ts, *p = NULL;
|
||||
struct timespec64 ts, *p = NULL;
|
||||
if (u_abs_timeout) {
|
||||
int res = compat_prepare_timeout(u_abs_timeout, &ts);
|
||||
if (res)
|
||||
|
@ -1504,7 +1504,7 @@ COMPAT_SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes,
|
|||
compat_size_t, msg_len, unsigned int __user *, u_msg_prio,
|
||||
const struct compat_timespec __user *, u_abs_timeout)
|
||||
{
|
||||
struct timespec ts, *p = NULL;
|
||||
struct timespec64 ts, *p = NULL;
|
||||
if (u_abs_timeout) {
|
||||
int res = compat_prepare_timeout(u_abs_timeout, &ts);
|
||||
if (res)
|
||||
|
|
372
ipc/msg.c
372
ipc/msg.c
|
@ -133,7 +133,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
|
|||
}
|
||||
|
||||
msq->q_stime = msq->q_rtime = 0;
|
||||
msq->q_ctime = get_seconds();
|
||||
msq->q_ctime = ktime_get_real_seconds();
|
||||
msq->q_cbytes = msq->q_qnum = 0;
|
||||
msq->q_qbytes = ns->msg_ctlmnb;
|
||||
msq->q_lspid = msq->q_lrpid = 0;
|
||||
|
@ -361,23 +361,17 @@ copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version)
|
|||
* NOTE: no locks must be held, the rwsem is taken inside this function.
|
||||
*/
|
||||
static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
|
||||
struct msqid_ds __user *buf, int version)
|
||||
struct msqid64_ds *msqid64)
|
||||
{
|
||||
struct kern_ipc_perm *ipcp;
|
||||
struct msqid64_ds uninitialized_var(msqid64);
|
||||
struct msg_queue *msq;
|
||||
int err;
|
||||
|
||||
if (cmd == IPC_SET) {
|
||||
if (copy_msqid_from_user(&msqid64, buf, version))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
down_write(&msg_ids(ns).rwsem);
|
||||
rcu_read_lock();
|
||||
|
||||
ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd,
|
||||
&msqid64.msg_perm, msqid64.msg_qbytes);
|
||||
&msqid64->msg_perm, msqid64->msg_qbytes);
|
||||
if (IS_ERR(ipcp)) {
|
||||
err = PTR_ERR(ipcp);
|
||||
goto out_unlock1;
|
||||
|
@ -399,20 +393,20 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
|
|||
{
|
||||
DEFINE_WAKE_Q(wake_q);
|
||||
|
||||
if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
|
||||
if (msqid64->msg_qbytes > ns->msg_ctlmnb &&
|
||||
!capable(CAP_SYS_RESOURCE)) {
|
||||
err = -EPERM;
|
||||
goto out_unlock1;
|
||||
}
|
||||
|
||||
ipc_lock_object(&msq->q_perm);
|
||||
err = ipc_update_perm(&msqid64.msg_perm, ipcp);
|
||||
err = ipc_update_perm(&msqid64->msg_perm, ipcp);
|
||||
if (err)
|
||||
goto out_unlock0;
|
||||
|
||||
msq->q_qbytes = msqid64.msg_qbytes;
|
||||
msq->q_qbytes = msqid64->msg_qbytes;
|
||||
|
||||
msq->q_ctime = get_seconds();
|
||||
msq->q_ctime = ktime_get_real_seconds();
|
||||
/*
|
||||
* Sleeping receivers might be excluded by
|
||||
* stricter permissions.
|
||||
|
@ -442,111 +436,89 @@ out_up:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int msgctl_nolock(struct ipc_namespace *ns, int msqid,
|
||||
int cmd, int version, void __user *buf)
|
||||
static int msgctl_info(struct ipc_namespace *ns, int msqid,
|
||||
int cmd, struct msginfo *msginfo)
|
||||
{
|
||||
int err;
|
||||
int max_id;
|
||||
|
||||
/*
|
||||
* We must not return kernel stack data.
|
||||
* due to padding, it's not enough
|
||||
* to set all member fields.
|
||||
*/
|
||||
err = security_msg_queue_msgctl(NULL, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(msginfo, 0, sizeof(*msginfo));
|
||||
msginfo->msgmni = ns->msg_ctlmni;
|
||||
msginfo->msgmax = ns->msg_ctlmax;
|
||||
msginfo->msgmnb = ns->msg_ctlmnb;
|
||||
msginfo->msgssz = MSGSSZ;
|
||||
msginfo->msgseg = MSGSEG;
|
||||
down_read(&msg_ids(ns).rwsem);
|
||||
if (cmd == MSG_INFO) {
|
||||
msginfo->msgpool = msg_ids(ns).in_use;
|
||||
msginfo->msgmap = atomic_read(&ns->msg_hdrs);
|
||||
msginfo->msgtql = atomic_read(&ns->msg_bytes);
|
||||
} else {
|
||||
msginfo->msgmap = MSGMAP;
|
||||
msginfo->msgpool = MSGPOOL;
|
||||
msginfo->msgtql = MSGTQL;
|
||||
}
|
||||
max_id = ipc_get_maxid(&msg_ids(ns));
|
||||
up_read(&msg_ids(ns).rwsem);
|
||||
return (max_id < 0) ? 0 : max_id;
|
||||
}
|
||||
|
||||
static int msgctl_stat(struct ipc_namespace *ns, int msqid,
|
||||
int cmd, struct msqid64_ds *p)
|
||||
{
|
||||
int err;
|
||||
struct msg_queue *msq;
|
||||
int success_return;
|
||||
|
||||
switch (cmd) {
|
||||
case IPC_INFO:
|
||||
case MSG_INFO:
|
||||
{
|
||||
struct msginfo msginfo;
|
||||
int max_id;
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
if (!buf)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* We must not return kernel stack data.
|
||||
* due to padding, it's not enough
|
||||
* to set all member fields.
|
||||
*/
|
||||
err = security_msg_queue_msgctl(NULL, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(&msginfo, 0, sizeof(msginfo));
|
||||
msginfo.msgmni = ns->msg_ctlmni;
|
||||
msginfo.msgmax = ns->msg_ctlmax;
|
||||
msginfo.msgmnb = ns->msg_ctlmnb;
|
||||
msginfo.msgssz = MSGSSZ;
|
||||
msginfo.msgseg = MSGSEG;
|
||||
down_read(&msg_ids(ns).rwsem);
|
||||
if (cmd == MSG_INFO) {
|
||||
msginfo.msgpool = msg_ids(ns).in_use;
|
||||
msginfo.msgmap = atomic_read(&ns->msg_hdrs);
|
||||
msginfo.msgtql = atomic_read(&ns->msg_bytes);
|
||||
} else {
|
||||
msginfo.msgmap = MSGMAP;
|
||||
msginfo.msgpool = MSGPOOL;
|
||||
msginfo.msgtql = MSGTQL;
|
||||
}
|
||||
max_id = ipc_get_maxid(&msg_ids(ns));
|
||||
up_read(&msg_ids(ns).rwsem);
|
||||
if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
|
||||
return -EFAULT;
|
||||
return (max_id < 0) ? 0 : max_id;
|
||||
}
|
||||
|
||||
case MSG_STAT:
|
||||
case IPC_STAT:
|
||||
{
|
||||
struct msqid64_ds tbuf;
|
||||
int success_return;
|
||||
|
||||
if (!buf)
|
||||
return -EFAULT;
|
||||
|
||||
memset(&tbuf, 0, sizeof(tbuf));
|
||||
|
||||
rcu_read_lock();
|
||||
if (cmd == MSG_STAT) {
|
||||
msq = msq_obtain_object(ns, msqid);
|
||||
if (IS_ERR(msq)) {
|
||||
err = PTR_ERR(msq);
|
||||
goto out_unlock;
|
||||
}
|
||||
success_return = msq->q_perm.id;
|
||||
} else {
|
||||
msq = msq_obtain_object_check(ns, msqid);
|
||||
if (IS_ERR(msq)) {
|
||||
err = PTR_ERR(msq);
|
||||
goto out_unlock;
|
||||
}
|
||||
success_return = 0;
|
||||
}
|
||||
|
||||
err = -EACCES;
|
||||
if (ipcperms(ns, &msq->q_perm, S_IRUGO))
|
||||
rcu_read_lock();
|
||||
if (cmd == MSG_STAT) {
|
||||
msq = msq_obtain_object(ns, msqid);
|
||||
if (IS_ERR(msq)) {
|
||||
err = PTR_ERR(msq);
|
||||
goto out_unlock;
|
||||
|
||||
err = security_msg_queue_msgctl(msq, cmd);
|
||||
if (err)
|
||||
}
|
||||
success_return = msq->q_perm.id;
|
||||
} else {
|
||||
msq = msq_obtain_object_check(ns, msqid);
|
||||
if (IS_ERR(msq)) {
|
||||
err = PTR_ERR(msq);
|
||||
goto out_unlock;
|
||||
|
||||
kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);
|
||||
tbuf.msg_stime = msq->q_stime;
|
||||
tbuf.msg_rtime = msq->q_rtime;
|
||||
tbuf.msg_ctime = msq->q_ctime;
|
||||
tbuf.msg_cbytes = msq->q_cbytes;
|
||||
tbuf.msg_qnum = msq->q_qnum;
|
||||
tbuf.msg_qbytes = msq->q_qbytes;
|
||||
tbuf.msg_lspid = msq->q_lspid;
|
||||
tbuf.msg_lrpid = msq->q_lrpid;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (copy_msqid_to_user(buf, &tbuf, version))
|
||||
return -EFAULT;
|
||||
return success_return;
|
||||
}
|
||||
success_return = 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
err = -EACCES;
|
||||
if (ipcperms(ns, &msq->q_perm, S_IRUGO))
|
||||
goto out_unlock;
|
||||
|
||||
err = security_msg_queue_msgctl(msq, cmd);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm);
|
||||
p->msg_stime = msq->q_stime;
|
||||
p->msg_rtime = msq->q_rtime;
|
||||
p->msg_ctime = msq->q_ctime;
|
||||
p->msg_cbytes = msq->q_cbytes;
|
||||
p->msg_qnum = msq->q_qnum;
|
||||
p->msg_qbytes = msq->q_qbytes;
|
||||
p->msg_lspid = msq->q_lspid;
|
||||
p->msg_lrpid = msq->q_lrpid;
|
||||
rcu_read_unlock();
|
||||
|
||||
return success_return;
|
||||
|
||||
return err;
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
|
@ -556,6 +528,8 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
|
|||
{
|
||||
int version;
|
||||
struct ipc_namespace *ns;
|
||||
struct msqid64_ds msqid64;
|
||||
int err;
|
||||
|
||||
if (msqid < 0 || cmd < 0)
|
||||
return -EINVAL;
|
||||
|
@ -565,18 +539,147 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
|
|||
|
||||
switch (cmd) {
|
||||
case IPC_INFO:
|
||||
case MSG_INFO:
|
||||
case MSG_INFO: {
|
||||
struct msginfo msginfo;
|
||||
err = msgctl_info(ns, msqid, cmd, &msginfo);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
case MSG_STAT: /* msqid is an index rather than a msg queue id */
|
||||
case IPC_STAT:
|
||||
return msgctl_nolock(ns, msqid, cmd, version, buf);
|
||||
err = msgctl_stat(ns, msqid, cmd, &msqid64);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_msqid_to_user(buf, &msqid64, version))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
case IPC_SET:
|
||||
if (copy_msqid_from_user(&msqid64, buf, version))
|
||||
return -EFAULT;
|
||||
/* fallthru */
|
||||
case IPC_RMID:
|
||||
return msgctl_down(ns, msqid, cmd, buf, version);
|
||||
return msgctl_down(ns, msqid, cmd, &msqid64);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
static int copy_compat_msqid_from_user(struct msqid64_ds *out, void __user *buf,
|
||||
int version)
|
||||
{
|
||||
memset(out, 0, sizeof(*out));
|
||||
if (version == IPC_64) {
|
||||
struct compat_msqid64_ds *p = buf;
|
||||
if (get_compat_ipc64_perm(&out->msg_perm, &p->msg_perm))
|
||||
return -EFAULT;
|
||||
if (get_user(out->msg_qbytes, &p->msg_qbytes))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
struct compat_msqid_ds *p = buf;
|
||||
if (get_compat_ipc_perm(&out->msg_perm, &p->msg_perm))
|
||||
return -EFAULT;
|
||||
if (get_user(out->msg_qbytes, &p->msg_qbytes))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in,
|
||||
int version)
|
||||
{
|
||||
if (version == IPC_64) {
|
||||
struct compat_msqid64_ds v;
|
||||
memset(&v, 0, sizeof(v));
|
||||
to_compat_ipc64_perm(&v.msg_perm, &in->msg_perm);
|
||||
v.msg_stime = in->msg_stime;
|
||||
v.msg_rtime = in->msg_rtime;
|
||||
v.msg_ctime = in->msg_ctime;
|
||||
v.msg_cbytes = in->msg_cbytes;
|
||||
v.msg_qnum = in->msg_qnum;
|
||||
v.msg_qbytes = in->msg_qbytes;
|
||||
v.msg_lspid = in->msg_lspid;
|
||||
v.msg_lrpid = in->msg_lrpid;
|
||||
return copy_to_user(buf, &v, sizeof(v));
|
||||
} else {
|
||||
struct compat_msqid_ds v;
|
||||
memset(&v, 0, sizeof(v));
|
||||
to_compat_ipc_perm(&v.msg_perm, &in->msg_perm);
|
||||
v.msg_stime = in->msg_stime;
|
||||
v.msg_rtime = in->msg_rtime;
|
||||
v.msg_ctime = in->msg_ctime;
|
||||
v.msg_cbytes = in->msg_cbytes;
|
||||
v.msg_qnum = in->msg_qnum;
|
||||
v.msg_qbytes = in->msg_qbytes;
|
||||
v.msg_lspid = in->msg_lspid;
|
||||
v.msg_lrpid = in->msg_lrpid;
|
||||
return copy_to_user(buf, &v, sizeof(v));
|
||||
}
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, void __user *, uptr)
|
||||
{
|
||||
struct ipc_namespace *ns;
|
||||
int err;
|
||||
struct msqid64_ds msqid64;
|
||||
int version = compat_ipc_parse_version(&cmd);
|
||||
|
||||
ns = current->nsproxy->ipc_ns;
|
||||
|
||||
if (msqid < 0 || cmd < 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (cmd & (~IPC_64)) {
|
||||
case IPC_INFO:
|
||||
case MSG_INFO: {
|
||||
struct msginfo msginfo;
|
||||
err = msgctl_info(ns, msqid, cmd, &msginfo);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_to_user(uptr, &msginfo, sizeof(struct msginfo)))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
case IPC_STAT:
|
||||
case MSG_STAT:
|
||||
err = msgctl_stat(ns, msqid, cmd, &msqid64);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_compat_msqid_to_user(uptr, &msqid64, version))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
case IPC_SET:
|
||||
if (copy_compat_msqid_from_user(&msqid64, uptr, version))
|
||||
return -EFAULT;
|
||||
/* fallthru */
|
||||
case IPC_RMID:
|
||||
return msgctl_down(ns, msqid, cmd, &msqid64);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int testmsg(struct msg_msg *msg, long type, int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
|
@ -627,7 +730,7 @@ static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
long do_msgsnd(int msqid, long mtype, void __user *mtext,
|
||||
static long do_msgsnd(int msqid, long mtype, void __user *mtext,
|
||||
size_t msgsz, int msgflg)
|
||||
{
|
||||
struct msg_queue *msq;
|
||||
|
@ -750,6 +853,25 @@ SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
|
|||
return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
struct compat_msgbuf {
|
||||
compat_long_t mtype;
|
||||
char mtext[1];
|
||||
};
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp,
|
||||
compat_ssize_t, msgsz, int, msgflg)
|
||||
{
|
||||
struct compat_msgbuf __user *up = compat_ptr(msgp);
|
||||
compat_long_t mtype;
|
||||
|
||||
if (get_user(mtype, &up->mtype))
|
||||
return -EFAULT;
|
||||
return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int convert_mode(long *msgtyp, int msgflg)
|
||||
{
|
||||
if (msgflg & MSG_COPY)
|
||||
|
@ -846,7 +968,7 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
|
|||
return found ?: ERR_PTR(-EAGAIN);
|
||||
}
|
||||
|
||||
long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
|
||||
static long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
|
||||
long (*msg_handler)(void __user *, struct msg_msg *, size_t))
|
||||
{
|
||||
int mode;
|
||||
|
@ -1010,6 +1132,28 @@ SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
|
|||
return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
|
||||
{
|
||||
struct compat_msgbuf __user *msgp = dest;
|
||||
size_t msgsz;
|
||||
|
||||
if (put_user(msg->m_type, &msgp->mtype))
|
||||
return -EFAULT;
|
||||
|
||||
msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz;
|
||||
if (store_msg(msgp->mtext, msg, msgsz))
|
||||
return -EFAULT;
|
||||
return msgsz;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp,
|
||||
compat_ssize_t, msgsz, compat_long_t, msgtyp, int, msgflg)
|
||||
{
|
||||
return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp,
|
||||
msgflg, compat_do_msg_fill);
|
||||
}
|
||||
#endif
|
||||
|
||||
int msg_init_ns(struct ipc_namespace *ns)
|
||||
{
|
||||
|
@ -1039,7 +1183,7 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
|
|||
struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
|
||||
|
||||
seq_printf(s,
|
||||
"%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
|
||||
"%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10llu %10llu %10llu\n",
|
||||
msq->q_perm.key,
|
||||
msq->q_perm.id,
|
||||
msq->q_perm.mode,
|
||||
|
|
354
ipc/sem.c
354
ipc/sem.c
|
@ -512,7 +512,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
|
|||
INIT_LIST_HEAD(&sma->pending_const);
|
||||
INIT_LIST_HEAD(&sma->list_id);
|
||||
sma->sem_nsems = nsems;
|
||||
sma->sem_ctime = get_seconds();
|
||||
sma->sem_ctime = ktime_get_real_seconds();
|
||||
|
||||
retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
|
||||
if (retval < 0) {
|
||||
|
@ -1163,14 +1163,14 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in,
|
|||
}
|
||||
}
|
||||
|
||||
static time_t get_semotime(struct sem_array *sma)
|
||||
static time64_t get_semotime(struct sem_array *sma)
|
||||
{
|
||||
int i;
|
||||
time_t res;
|
||||
time64_t res;
|
||||
|
||||
res = sma->sems[0].sem_otime;
|
||||
for (i = 1; i < sma->sem_nsems; i++) {
|
||||
time_t to = sma->sems[i].sem_otime;
|
||||
time64_t to = sma->sems[i].sem_otime;
|
||||
|
||||
if (to > res)
|
||||
res = to;
|
||||
|
@ -1178,112 +1178,95 @@ static time_t get_semotime(struct sem_array *sma)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int semctl_nolock(struct ipc_namespace *ns, int semid,
|
||||
int cmd, int version, void __user *p)
|
||||
static int semctl_stat(struct ipc_namespace *ns, int semid,
|
||||
int cmd, struct semid64_ds *semid64)
|
||||
{
|
||||
int err;
|
||||
struct sem_array *sma;
|
||||
int id = 0;
|
||||
int err;
|
||||
|
||||
switch (cmd) {
|
||||
case IPC_INFO:
|
||||
case SEM_INFO:
|
||||
{
|
||||
struct seminfo seminfo;
|
||||
int max_id;
|
||||
memset(semid64, 0, sizeof(*semid64));
|
||||
|
||||
err = security_sem_semctl(NULL, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(&seminfo, 0, sizeof(seminfo));
|
||||
seminfo.semmni = ns->sc_semmni;
|
||||
seminfo.semmns = ns->sc_semmns;
|
||||
seminfo.semmsl = ns->sc_semmsl;
|
||||
seminfo.semopm = ns->sc_semopm;
|
||||
seminfo.semvmx = SEMVMX;
|
||||
seminfo.semmnu = SEMMNU;
|
||||
seminfo.semmap = SEMMAP;
|
||||
seminfo.semume = SEMUME;
|
||||
down_read(&sem_ids(ns).rwsem);
|
||||
if (cmd == SEM_INFO) {
|
||||
seminfo.semusz = sem_ids(ns).in_use;
|
||||
seminfo.semaem = ns->used_sems;
|
||||
} else {
|
||||
seminfo.semusz = SEMUSZ;
|
||||
seminfo.semaem = SEMAEM;
|
||||
}
|
||||
max_id = ipc_get_maxid(&sem_ids(ns));
|
||||
up_read(&sem_ids(ns).rwsem);
|
||||
if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
|
||||
return -EFAULT;
|
||||
return (max_id < 0) ? 0 : max_id;
|
||||
}
|
||||
case IPC_STAT:
|
||||
case SEM_STAT:
|
||||
{
|
||||
struct semid64_ds tbuf;
|
||||
int id = 0;
|
||||
|
||||
memset(&tbuf, 0, sizeof(tbuf));
|
||||
|
||||
rcu_read_lock();
|
||||
if (cmd == SEM_STAT) {
|
||||
sma = sem_obtain_object(ns, semid);
|
||||
if (IS_ERR(sma)) {
|
||||
err = PTR_ERR(sma);
|
||||
goto out_unlock;
|
||||
}
|
||||
id = sma->sem_perm.id;
|
||||
} else {
|
||||
sma = sem_obtain_object_check(ns, semid);
|
||||
if (IS_ERR(sma)) {
|
||||
err = PTR_ERR(sma);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
err = -EACCES;
|
||||
if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
|
||||
rcu_read_lock();
|
||||
if (cmd == SEM_STAT) {
|
||||
sma = sem_obtain_object(ns, semid);
|
||||
if (IS_ERR(sma)) {
|
||||
err = PTR_ERR(sma);
|
||||
goto out_unlock;
|
||||
|
||||
err = security_sem_semctl(sma, cmd);
|
||||
if (err)
|
||||
}
|
||||
id = sma->sem_perm.id;
|
||||
} else {
|
||||
sma = sem_obtain_object_check(ns, semid);
|
||||
if (IS_ERR(sma)) {
|
||||
err = PTR_ERR(sma);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
err = -EACCES;
|
||||
if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
|
||||
goto out_unlock;
|
||||
|
||||
err = security_sem_semctl(sma, cmd);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm);
|
||||
semid64->sem_otime = get_semotime(sma);
|
||||
semid64->sem_ctime = sma->sem_ctime;
|
||||
semid64->sem_nsems = sma->sem_nsems;
|
||||
rcu_read_unlock();
|
||||
return id;
|
||||
|
||||
kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
|
||||
tbuf.sem_otime = get_semotime(sma);
|
||||
tbuf.sem_ctime = sma->sem_ctime;
|
||||
tbuf.sem_nsems = sma->sem_nsems;
|
||||
rcu_read_unlock();
|
||||
if (copy_semid_to_user(p, &tbuf, version))
|
||||
return -EFAULT;
|
||||
return id;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
static int semctl_info(struct ipc_namespace *ns, int semid,
|
||||
int cmd, void __user *p)
|
||||
{
|
||||
struct seminfo seminfo;
|
||||
int max_id;
|
||||
int err;
|
||||
|
||||
err = security_sem_semctl(NULL, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(&seminfo, 0, sizeof(seminfo));
|
||||
seminfo.semmni = ns->sc_semmni;
|
||||
seminfo.semmns = ns->sc_semmns;
|
||||
seminfo.semmsl = ns->sc_semmsl;
|
||||
seminfo.semopm = ns->sc_semopm;
|
||||
seminfo.semvmx = SEMVMX;
|
||||
seminfo.semmnu = SEMMNU;
|
||||
seminfo.semmap = SEMMAP;
|
||||
seminfo.semume = SEMUME;
|
||||
down_read(&sem_ids(ns).rwsem);
|
||||
if (cmd == SEM_INFO) {
|
||||
seminfo.semusz = sem_ids(ns).in_use;
|
||||
seminfo.semaem = ns->used_sems;
|
||||
} else {
|
||||
seminfo.semusz = SEMUSZ;
|
||||
seminfo.semaem = SEMAEM;
|
||||
}
|
||||
max_id = ipc_get_maxid(&sem_ids(ns));
|
||||
up_read(&sem_ids(ns).rwsem);
|
||||
if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
|
||||
return -EFAULT;
|
||||
return (max_id < 0) ? 0 : max_id;
|
||||
}
|
||||
|
||||
static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
|
||||
unsigned long arg)
|
||||
int val)
|
||||
{
|
||||
struct sem_undo *un;
|
||||
struct sem_array *sma;
|
||||
struct sem *curr;
|
||||
int err, val;
|
||||
int err;
|
||||
DEFINE_WAKE_Q(wake_q);
|
||||
|
||||
#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN)
|
||||
/* big-endian 64bit */
|
||||
val = arg >> 32;
|
||||
#else
|
||||
/* 32bit or little-endian 64bit */
|
||||
val = arg;
|
||||
#endif
|
||||
|
||||
if (val > SEMVMX || val < 0)
|
||||
return -ERANGE;
|
||||
|
||||
|
@ -1327,7 +1310,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
|
|||
|
||||
curr->semval = val;
|
||||
curr->sempid = task_tgid_vnr(current);
|
||||
sma->sem_ctime = get_seconds();
|
||||
sma->sem_ctime = ktime_get_real_seconds();
|
||||
/* maybe some queued-up processes were waiting for this */
|
||||
do_smart_update(sma, NULL, 0, 0, &wake_q);
|
||||
sem_unlock(sma, -1);
|
||||
|
@ -1455,7 +1438,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
|
|||
for (i = 0; i < nsems; i++)
|
||||
un->semadj[i] = 0;
|
||||
}
|
||||
sma->sem_ctime = get_seconds();
|
||||
sma->sem_ctime = ktime_get_real_seconds();
|
||||
/* maybe some queued-up processes were waiting for this */
|
||||
do_smart_update(sma, NULL, 0, 0, &wake_q);
|
||||
err = 0;
|
||||
|
@ -1532,23 +1515,17 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version)
|
|||
* NOTE: no locks must be held, the rwsem is taken inside this function.
|
||||
*/
|
||||
static int semctl_down(struct ipc_namespace *ns, int semid,
|
||||
int cmd, int version, void __user *p)
|
||||
int cmd, struct semid64_ds *semid64)
|
||||
{
|
||||
struct sem_array *sma;
|
||||
int err;
|
||||
struct semid64_ds semid64;
|
||||
struct kern_ipc_perm *ipcp;
|
||||
|
||||
if (cmd == IPC_SET) {
|
||||
if (copy_semid_from_user(&semid64, p, version))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
down_write(&sem_ids(ns).rwsem);
|
||||
rcu_read_lock();
|
||||
|
||||
ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,
|
||||
&semid64.sem_perm, 0);
|
||||
&semid64->sem_perm, 0);
|
||||
if (IS_ERR(ipcp)) {
|
||||
err = PTR_ERR(ipcp);
|
||||
goto out_unlock1;
|
||||
|
@ -1568,10 +1545,10 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
|
|||
goto out_up;
|
||||
case IPC_SET:
|
||||
sem_lock(sma, NULL, -1);
|
||||
err = ipc_update_perm(&semid64.sem_perm, ipcp);
|
||||
err = ipc_update_perm(&semid64->sem_perm, ipcp);
|
||||
if (err)
|
||||
goto out_unlock0;
|
||||
sma->sem_ctime = get_seconds();
|
||||
sma->sem_ctime = ktime_get_real_seconds();
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
|
@ -1592,6 +1569,8 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
|
|||
int version;
|
||||
struct ipc_namespace *ns;
|
||||
void __user *p = (void __user *)arg;
|
||||
struct semid64_ds semid64;
|
||||
int err;
|
||||
|
||||
if (semid < 0)
|
||||
return -EINVAL;
|
||||
|
@ -1602,9 +1581,15 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
|
|||
switch (cmd) {
|
||||
case IPC_INFO:
|
||||
case SEM_INFO:
|
||||
return semctl_info(ns, semid, cmd, p);
|
||||
case IPC_STAT:
|
||||
case SEM_STAT:
|
||||
return semctl_nolock(ns, semid, cmd, version, p);
|
||||
err = semctl_stat(ns, semid, cmd, &semid64);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_semid_to_user(p, &semid64, version))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
case GETALL:
|
||||
case GETVAL:
|
||||
case GETPID:
|
||||
|
@ -1612,16 +1597,121 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
|
|||
case GETZCNT:
|
||||
case SETALL:
|
||||
return semctl_main(ns, semid, semnum, cmd, p);
|
||||
case SETVAL:
|
||||
return semctl_setval(ns, semid, semnum, arg);
|
||||
case IPC_RMID:
|
||||
case SETVAL: {
|
||||
int val;
|
||||
#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN)
|
||||
/* big-endian 64bit */
|
||||
val = arg >> 32;
|
||||
#else
|
||||
/* 32bit or little-endian 64bit */
|
||||
val = arg;
|
||||
#endif
|
||||
return semctl_setval(ns, semid, semnum, val);
|
||||
}
|
||||
case IPC_SET:
|
||||
return semctl_down(ns, semid, cmd, version, p);
|
||||
if (copy_semid_from_user(&semid64, p, version))
|
||||
return -EFAULT;
|
||||
case IPC_RMID:
|
||||
return semctl_down(ns, semid, cmd, &semid64);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
static int copy_compat_semid_from_user(struct semid64_ds *out, void __user *buf,
|
||||
int version)
|
||||
{
|
||||
memset(out, 0, sizeof(*out));
|
||||
if (version == IPC_64) {
|
||||
struct compat_semid64_ds *p = buf;
|
||||
return get_compat_ipc64_perm(&out->sem_perm, &p->sem_perm);
|
||||
} else {
|
||||
struct compat_semid_ds *p = buf;
|
||||
return get_compat_ipc_perm(&out->sem_perm, &p->sem_perm);
|
||||
}
|
||||
}
|
||||
|
||||
static int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in,
|
||||
int version)
|
||||
{
|
||||
if (version == IPC_64) {
|
||||
struct compat_semid64_ds v;
|
||||
memset(&v, 0, sizeof(v));
|
||||
to_compat_ipc64_perm(&v.sem_perm, &in->sem_perm);
|
||||
v.sem_otime = in->sem_otime;
|
||||
v.sem_ctime = in->sem_ctime;
|
||||
v.sem_nsems = in->sem_nsems;
|
||||
return copy_to_user(buf, &v, sizeof(v));
|
||||
} else {
|
||||
struct compat_semid_ds v;
|
||||
memset(&v, 0, sizeof(v));
|
||||
to_compat_ipc_perm(&v.sem_perm, &in->sem_perm);
|
||||
v.sem_otime = in->sem_otime;
|
||||
v.sem_ctime = in->sem_ctime;
|
||||
v.sem_nsems = in->sem_nsems;
|
||||
return copy_to_user(buf, &v, sizeof(v));
|
||||
}
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg)
|
||||
{
|
||||
void __user *p = compat_ptr(arg);
|
||||
struct ipc_namespace *ns;
|
||||
struct semid64_ds semid64;
|
||||
int version = compat_ipc_parse_version(&cmd);
|
||||
int err;
|
||||
|
||||
ns = current->nsproxy->ipc_ns;
|
||||
|
||||
if (semid < 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (cmd & (~IPC_64)) {
|
||||
case IPC_INFO:
|
||||
case SEM_INFO:
|
||||
return semctl_info(ns, semid, cmd, p);
|
||||
case IPC_STAT:
|
||||
case SEM_STAT:
|
||||
err = semctl_stat(ns, semid, cmd, &semid64);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_compat_semid_to_user(p, &semid64, version))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
case GETVAL:
|
||||
case GETPID:
|
||||
case GETNCNT:
|
||||
case GETZCNT:
|
||||
case GETALL:
|
||||
case SETALL:
|
||||
return semctl_main(ns, semid, semnum, cmd, p);
|
||||
case SETVAL:
|
||||
return semctl_setval(ns, semid, semnum, arg);
|
||||
case IPC_SET:
|
||||
if (copy_compat_semid_from_user(&semid64, p, version))
|
||||
return -EFAULT;
|
||||
/* fallthru */
|
||||
case IPC_RMID:
|
||||
return semctl_down(ns, semid, cmd, &semid64);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the task doesn't already have a undo_list, then allocate one
|
||||
* here. We guarantee there is only one thread using this undo list,
|
||||
* and current is THE ONE
|
||||
|
@ -1766,8 +1856,8 @@ out:
|
|||
return un;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
|
||||
unsigned, nsops, const struct timespec __user *, timeout)
|
||||
static long do_semtimedop(int semid, struct sembuf __user *tsops,
|
||||
unsigned nsops, const struct timespec64 *timeout)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
struct sem_array *sma;
|
||||
|
@ -1798,17 +1888,12 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
|
|||
}
|
||||
|
||||
if (timeout) {
|
||||
struct timespec _timeout;
|
||||
if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) {
|
||||
error = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
|
||||
_timeout.tv_nsec >= 1000000000L) {
|
||||
if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 ||
|
||||
timeout->tv_nsec >= 1000000000L) {
|
||||
error = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
jiffies_left = timespec_to_jiffies(&_timeout);
|
||||
jiffies_left = timespec64_to_jiffies(timeout);
|
||||
}
|
||||
|
||||
max = 0;
|
||||
|
@ -2023,10 +2108,37 @@ out_free:
|
|||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
|
||||
unsigned, nsops, const struct timespec __user *, timeout)
|
||||
{
|
||||
if (timeout) {
|
||||
struct timespec64 ts;
|
||||
if (get_timespec64(&ts, timeout))
|
||||
return -EFAULT;
|
||||
return do_semtimedop(semid, tsops, nsops, &ts);
|
||||
}
|
||||
return do_semtimedop(semid, tsops, nsops, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems,
|
||||
unsigned, nsops,
|
||||
const struct compat_timespec __user *, timeout)
|
||||
{
|
||||
if (timeout) {
|
||||
struct timespec64 ts;
|
||||
if (compat_get_timespec64(&ts, timeout))
|
||||
return -EFAULT;
|
||||
return do_semtimedop(semid, tsems, nsops, &ts);
|
||||
}
|
||||
return do_semtimedop(semid, tsems, nsops, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,
|
||||
unsigned, nsops)
|
||||
{
|
||||
return sys_semtimedop(semid, tsops, nsops, NULL);
|
||||
return do_semtimedop(semid, tsops, nsops, NULL);
|
||||
}
|
||||
|
||||
/* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between
|
||||
|
@ -2183,7 +2295,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
|
|||
struct user_namespace *user_ns = seq_user_ns(s);
|
||||
struct kern_ipc_perm *ipcp = it;
|
||||
struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
|
||||
time_t sem_otime;
|
||||
time64_t sem_otime;
|
||||
|
||||
/*
|
||||
* The proc interface isn't aware of sem_lock(), it calls
|
||||
|
@ -2196,7 +2308,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
|
|||
sem_otime = get_semotime(sma);
|
||||
|
||||
seq_printf(s,
|
||||
"%10d %10d %4o %10u %5u %5u %5u %5u %10lu %10lu\n",
|
||||
"%10d %10d %4o %10u %5u %5u %5u %5u %10llu %10llu\n",
|
||||
sma->sem_perm.key,
|
||||
sma->sem_perm.id,
|
||||
sma->sem_perm.mode,
|
||||
|
|
561
ipc/shm.c
561
ipc/shm.c
|
@ -202,7 +202,7 @@ static int __shm_open(struct vm_area_struct *vma)
|
|||
if (IS_ERR(shp))
|
||||
return PTR_ERR(shp);
|
||||
|
||||
shp->shm_atim = get_seconds();
|
||||
shp->shm_atim = ktime_get_real_seconds();
|
||||
shp->shm_lprid = task_tgid_vnr(current);
|
||||
shp->shm_nattch++;
|
||||
shm_unlock(shp);
|
||||
|
@ -289,7 +289,7 @@ static void shm_close(struct vm_area_struct *vma)
|
|||
goto done; /* no-op */
|
||||
|
||||
shp->shm_lprid = task_tgid_vnr(current);
|
||||
shp->shm_dtim = get_seconds();
|
||||
shp->shm_dtim = ktime_get_real_seconds();
|
||||
shp->shm_nattch--;
|
||||
if (shm_may_destroy(ns, shp))
|
||||
shm_destroy(ns, shp);
|
||||
|
@ -594,7 +594,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
|
|||
shp->shm_cprid = task_tgid_vnr(current);
|
||||
shp->shm_lprid = 0;
|
||||
shp->shm_atim = shp->shm_dtim = 0;
|
||||
shp->shm_ctim = get_seconds();
|
||||
shp->shm_ctim = ktime_get_real_seconds();
|
||||
shp->shm_segsz = size;
|
||||
shp->shm_nattch = 0;
|
||||
shp->shm_file = file;
|
||||
|
@ -815,23 +815,17 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
|
|||
* NOTE: no locks must be held, the rwsem is taken inside this function.
|
||||
*/
|
||||
static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
|
||||
struct shmid_ds __user *buf, int version)
|
||||
struct shmid64_ds *shmid64)
|
||||
{
|
||||
struct kern_ipc_perm *ipcp;
|
||||
struct shmid64_ds shmid64;
|
||||
struct shmid_kernel *shp;
|
||||
int err;
|
||||
|
||||
if (cmd == IPC_SET) {
|
||||
if (copy_shmid_from_user(&shmid64, buf, version))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
down_write(&shm_ids(ns).rwsem);
|
||||
rcu_read_lock();
|
||||
|
||||
ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd,
|
||||
&shmid64.shm_perm, 0);
|
||||
&shmid64->shm_perm, 0);
|
||||
if (IS_ERR(ipcp)) {
|
||||
err = PTR_ERR(ipcp);
|
||||
goto out_unlock1;
|
||||
|
@ -851,10 +845,10 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
|
|||
goto out_up;
|
||||
case IPC_SET:
|
||||
ipc_lock_object(&shp->shm_perm);
|
||||
err = ipc_update_perm(&shmid64.shm_perm, ipcp);
|
||||
err = ipc_update_perm(&shmid64->shm_perm, ipcp);
|
||||
if (err)
|
||||
goto out_unlock0;
|
||||
shp->shm_ctim = get_seconds();
|
||||
shp->shm_ctim = ktime_get_real_seconds();
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
|
@ -870,125 +864,175 @@ out_up:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int shmctl_nolock(struct ipc_namespace *ns, int shmid,
|
||||
int cmd, int version, void __user *buf)
|
||||
static int shmctl_ipc_info(struct ipc_namespace *ns,
|
||||
struct shminfo64 *shminfo)
|
||||
{
|
||||
int err;
|
||||
struct shmid_kernel *shp;
|
||||
|
||||
/* preliminary security checks for *_INFO */
|
||||
if (cmd == IPC_INFO || cmd == SHM_INFO) {
|
||||
err = security_shm_shmctl(NULL, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case IPC_INFO:
|
||||
{
|
||||
struct shminfo64 shminfo;
|
||||
|
||||
memset(&shminfo, 0, sizeof(shminfo));
|
||||
shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni;
|
||||
shminfo.shmmax = ns->shm_ctlmax;
|
||||
shminfo.shmall = ns->shm_ctlall;
|
||||
|
||||
shminfo.shmmin = SHMMIN;
|
||||
if (copy_shminfo_to_user(buf, &shminfo, version))
|
||||
return -EFAULT;
|
||||
|
||||
int err = security_shm_shmctl(NULL, IPC_INFO);
|
||||
if (!err) {
|
||||
memset(shminfo, 0, sizeof(*shminfo));
|
||||
shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni;
|
||||
shminfo->shmmax = ns->shm_ctlmax;
|
||||
shminfo->shmall = ns->shm_ctlall;
|
||||
shminfo->shmmin = SHMMIN;
|
||||
down_read(&shm_ids(ns).rwsem);
|
||||
err = ipc_get_maxid(&shm_ids(ns));
|
||||
up_read(&shm_ids(ns).rwsem);
|
||||
|
||||
if (err < 0)
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
case SHM_INFO:
|
||||
{
|
||||
struct shm_info shm_info;
|
||||
return err;
|
||||
}
|
||||
|
||||
memset(&shm_info, 0, sizeof(shm_info));
|
||||
static int shmctl_shm_info(struct ipc_namespace *ns,
|
||||
struct shm_info *shm_info)
|
||||
{
|
||||
int err = security_shm_shmctl(NULL, SHM_INFO);
|
||||
if (!err) {
|
||||
memset(shm_info, 0, sizeof(*shm_info));
|
||||
down_read(&shm_ids(ns).rwsem);
|
||||
shm_info.used_ids = shm_ids(ns).in_use;
|
||||
shm_get_stat(ns, &shm_info.shm_rss, &shm_info.shm_swp);
|
||||
shm_info.shm_tot = ns->shm_tot;
|
||||
shm_info.swap_attempts = 0;
|
||||
shm_info.swap_successes = 0;
|
||||
shm_info->used_ids = shm_ids(ns).in_use;
|
||||
shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp);
|
||||
shm_info->shm_tot = ns->shm_tot;
|
||||
shm_info->swap_attempts = 0;
|
||||
shm_info->swap_successes = 0;
|
||||
err = ipc_get_maxid(&shm_ids(ns));
|
||||
up_read(&shm_ids(ns).rwsem);
|
||||
if (copy_to_user(buf, &shm_info, sizeof(shm_info))) {
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = err < 0 ? 0 : err;
|
||||
goto out;
|
||||
if (err < 0)
|
||||
err = 0;
|
||||
}
|
||||
case SHM_STAT:
|
||||
case IPC_STAT:
|
||||
{
|
||||
struct shmid64_ds tbuf;
|
||||
int result;
|
||||
return err;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
if (cmd == SHM_STAT) {
|
||||
shp = shm_obtain_object(ns, shmid);
|
||||
if (IS_ERR(shp)) {
|
||||
err = PTR_ERR(shp);
|
||||
goto out_unlock;
|
||||
}
|
||||
result = shp->shm_perm.id;
|
||||
} else {
|
||||
shp = shm_obtain_object_check(ns, shmid);
|
||||
if (IS_ERR(shp)) {
|
||||
err = PTR_ERR(shp);
|
||||
goto out_unlock;
|
||||
}
|
||||
result = 0;
|
||||
}
|
||||
static int shmctl_stat(struct ipc_namespace *ns, int shmid,
|
||||
int cmd, struct shmid64_ds *tbuf)
|
||||
{
|
||||
struct shmid_kernel *shp;
|
||||
int result;
|
||||
int err;
|
||||
|
||||
err = -EACCES;
|
||||
if (ipcperms(ns, &shp->shm_perm, S_IRUGO))
|
||||
rcu_read_lock();
|
||||
if (cmd == SHM_STAT) {
|
||||
shp = shm_obtain_object(ns, shmid);
|
||||
if (IS_ERR(shp)) {
|
||||
err = PTR_ERR(shp);
|
||||
goto out_unlock;
|
||||
|
||||
err = security_shm_shmctl(shp, cmd);
|
||||
if (err)
|
||||
}
|
||||
result = shp->shm_perm.id;
|
||||
} else {
|
||||
shp = shm_obtain_object_check(ns, shmid);
|
||||
if (IS_ERR(shp)) {
|
||||
err = PTR_ERR(shp);
|
||||
goto out_unlock;
|
||||
|
||||
memset(&tbuf, 0, sizeof(tbuf));
|
||||
kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
|
||||
tbuf.shm_segsz = shp->shm_segsz;
|
||||
tbuf.shm_atime = shp->shm_atim;
|
||||
tbuf.shm_dtime = shp->shm_dtim;
|
||||
tbuf.shm_ctime = shp->shm_ctim;
|
||||
tbuf.shm_cpid = shp->shm_cprid;
|
||||
tbuf.shm_lpid = shp->shm_lprid;
|
||||
tbuf.shm_nattch = shp->shm_nattch;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (copy_shmid_to_user(buf, &tbuf, version))
|
||||
err = -EFAULT;
|
||||
else
|
||||
err = result;
|
||||
goto out;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
result = 0;
|
||||
}
|
||||
|
||||
err = -EACCES;
|
||||
if (ipcperms(ns, &shp->shm_perm, S_IRUGO))
|
||||
goto out_unlock;
|
||||
|
||||
err = security_shm_shmctl(shp, cmd);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
memset(tbuf, 0, sizeof(*tbuf));
|
||||
kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm);
|
||||
tbuf->shm_segsz = shp->shm_segsz;
|
||||
tbuf->shm_atime = shp->shm_atim;
|
||||
tbuf->shm_dtime = shp->shm_dtim;
|
||||
tbuf->shm_ctime = shp->shm_ctim;
|
||||
tbuf->shm_cpid = shp->shm_cprid;
|
||||
tbuf->shm_lpid = shp->shm_lprid;
|
||||
tbuf->shm_nattch = shp->shm_nattch;
|
||||
rcu_read_unlock();
|
||||
return result;
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd)
|
||||
{
|
||||
struct shmid_kernel *shp;
|
||||
struct file *shm_file;
|
||||
int err;
|
||||
|
||||
rcu_read_lock();
|
||||
shp = shm_obtain_object_check(ns, shmid);
|
||||
if (IS_ERR(shp)) {
|
||||
err = PTR_ERR(shp);
|
||||
goto out_unlock1;
|
||||
}
|
||||
|
||||
audit_ipc_obj(&(shp->shm_perm));
|
||||
err = security_shm_shmctl(shp, cmd);
|
||||
if (err)
|
||||
goto out_unlock1;
|
||||
|
||||
ipc_lock_object(&shp->shm_perm);
|
||||
|
||||
/* check if shm_destroy() is tearing down shp */
|
||||
if (!ipc_valid_object(&shp->shm_perm)) {
|
||||
err = -EIDRM;
|
||||
goto out_unlock0;
|
||||
}
|
||||
|
||||
if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
|
||||
kuid_t euid = current_euid();
|
||||
|
||||
if (!uid_eq(euid, shp->shm_perm.uid) &&
|
||||
!uid_eq(euid, shp->shm_perm.cuid)) {
|
||||
err = -EPERM;
|
||||
goto out_unlock0;
|
||||
}
|
||||
if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) {
|
||||
err = -EPERM;
|
||||
goto out_unlock0;
|
||||
}
|
||||
}
|
||||
|
||||
shm_file = shp->shm_file;
|
||||
if (is_file_hugepages(shm_file))
|
||||
goto out_unlock0;
|
||||
|
||||
if (cmd == SHM_LOCK) {
|
||||
struct user_struct *user = current_user();
|
||||
|
||||
err = shmem_lock(shm_file, 1, user);
|
||||
if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) {
|
||||
shp->shm_perm.mode |= SHM_LOCKED;
|
||||
shp->mlock_user = user;
|
||||
}
|
||||
goto out_unlock0;
|
||||
}
|
||||
|
||||
/* SHM_UNLOCK */
|
||||
if (!(shp->shm_perm.mode & SHM_LOCKED))
|
||||
goto out_unlock0;
|
||||
shmem_lock(shm_file, 0, shp->mlock_user);
|
||||
shp->shm_perm.mode &= ~SHM_LOCKED;
|
||||
shp->mlock_user = NULL;
|
||||
get_file(shm_file);
|
||||
ipc_unlock_object(&shp->shm_perm);
|
||||
rcu_read_unlock();
|
||||
shmem_unlock_mapping(shm_file->f_mapping);
|
||||
|
||||
fput(shm_file);
|
||||
return err;
|
||||
|
||||
out_unlock0:
|
||||
ipc_unlock_object(&shp->shm_perm);
|
||||
out_unlock1:
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
|
||||
{
|
||||
struct shmid_kernel *shp;
|
||||
int err, version;
|
||||
struct ipc_namespace *ns;
|
||||
struct shmid64_ds sem64;
|
||||
|
||||
if (cmd < 0 || shmid < 0)
|
||||
return -EINVAL;
|
||||
|
@ -997,92 +1041,222 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
|
|||
ns = current->nsproxy->ipc_ns;
|
||||
|
||||
switch (cmd) {
|
||||
case IPC_INFO:
|
||||
case SHM_INFO:
|
||||
case SHM_STAT:
|
||||
case IPC_STAT:
|
||||
return shmctl_nolock(ns, shmid, cmd, version, buf);
|
||||
case IPC_RMID:
|
||||
case IPC_SET:
|
||||
return shmctl_down(ns, shmid, cmd, buf, version);
|
||||
case SHM_LOCK:
|
||||
case SHM_UNLOCK:
|
||||
{
|
||||
struct file *shm_file;
|
||||
|
||||
rcu_read_lock();
|
||||
shp = shm_obtain_object_check(ns, shmid);
|
||||
if (IS_ERR(shp)) {
|
||||
err = PTR_ERR(shp);
|
||||
goto out_unlock1;
|
||||
}
|
||||
|
||||
audit_ipc_obj(&(shp->shm_perm));
|
||||
err = security_shm_shmctl(shp, cmd);
|
||||
if (err)
|
||||
goto out_unlock1;
|
||||
|
||||
ipc_lock_object(&shp->shm_perm);
|
||||
|
||||
/* check if shm_destroy() is tearing down shp */
|
||||
if (!ipc_valid_object(&shp->shm_perm)) {
|
||||
err = -EIDRM;
|
||||
goto out_unlock0;
|
||||
}
|
||||
|
||||
if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
|
||||
kuid_t euid = current_euid();
|
||||
|
||||
if (!uid_eq(euid, shp->shm_perm.uid) &&
|
||||
!uid_eq(euid, shp->shm_perm.cuid)) {
|
||||
err = -EPERM;
|
||||
goto out_unlock0;
|
||||
}
|
||||
if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) {
|
||||
err = -EPERM;
|
||||
goto out_unlock0;
|
||||
}
|
||||
}
|
||||
|
||||
shm_file = shp->shm_file;
|
||||
if (is_file_hugepages(shm_file))
|
||||
goto out_unlock0;
|
||||
|
||||
if (cmd == SHM_LOCK) {
|
||||
struct user_struct *user = current_user();
|
||||
|
||||
err = shmem_lock(shm_file, 1, user);
|
||||
if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) {
|
||||
shp->shm_perm.mode |= SHM_LOCKED;
|
||||
shp->mlock_user = user;
|
||||
}
|
||||
goto out_unlock0;
|
||||
}
|
||||
|
||||
/* SHM_UNLOCK */
|
||||
if (!(shp->shm_perm.mode & SHM_LOCKED))
|
||||
goto out_unlock0;
|
||||
shmem_lock(shm_file, 0, shp->mlock_user);
|
||||
shp->shm_perm.mode &= ~SHM_LOCKED;
|
||||
shp->mlock_user = NULL;
|
||||
get_file(shm_file);
|
||||
ipc_unlock_object(&shp->shm_perm);
|
||||
rcu_read_unlock();
|
||||
shmem_unlock_mapping(shm_file->f_mapping);
|
||||
|
||||
fput(shm_file);
|
||||
case IPC_INFO: {
|
||||
struct shminfo64 shminfo;
|
||||
err = shmctl_ipc_info(ns, &shminfo);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_shminfo_to_user(buf, &shminfo, version))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
case SHM_INFO: {
|
||||
struct shm_info shm_info;
|
||||
err = shmctl_shm_info(ns, &shm_info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_to_user(buf, &shm_info, sizeof(shm_info)))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
case SHM_STAT:
|
||||
case IPC_STAT: {
|
||||
err = shmctl_stat(ns, shmid, cmd, &sem64);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_shmid_to_user(buf, &sem64, version))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
case IPC_SET:
|
||||
if (copy_shmid_from_user(&sem64, buf, version))
|
||||
return -EFAULT;
|
||||
/* fallthru */
|
||||
case IPC_RMID:
|
||||
return shmctl_down(ns, shmid, cmd, &sem64);
|
||||
case SHM_LOCK:
|
||||
case SHM_UNLOCK:
|
||||
return shmctl_do_lock(ns, shmid, cmd);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock0:
|
||||
ipc_unlock_object(&shp->shm_perm);
|
||||
out_unlock1:
|
||||
rcu_read_unlock();
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
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_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;
|
||||
};
|
||||
|
||||
static int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in,
|
||||
int version)
|
||||
{
|
||||
if (in->shmmax > INT_MAX)
|
||||
in->shmmax = INT_MAX;
|
||||
if (version == IPC_64) {
|
||||
struct compat_shminfo64 info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.shmmax = in->shmmax;
|
||||
info.shmmin = in->shmmin;
|
||||
info.shmmni = in->shmmni;
|
||||
info.shmseg = in->shmseg;
|
||||
info.shmall = in->shmall;
|
||||
return copy_to_user(buf, &info, sizeof(info));
|
||||
} else {
|
||||
struct shminfo info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.shmmax = in->shmmax;
|
||||
info.shmmin = in->shmmin;
|
||||
info.shmmni = in->shmmni;
|
||||
info.shmseg = in->shmseg;
|
||||
info.shmall = in->shmall;
|
||||
return copy_to_user(buf, &info, sizeof(info));
|
||||
}
|
||||
}
|
||||
|
||||
static int put_compat_shm_info(struct shm_info *ip,
|
||||
struct compat_shm_info __user *uip)
|
||||
{
|
||||
struct compat_shm_info info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.used_ids = ip->used_ids;
|
||||
info.shm_tot = ip->shm_tot;
|
||||
info.shm_rss = ip->shm_rss;
|
||||
info.shm_swp = ip->shm_swp;
|
||||
info.swap_attempts = ip->swap_attempts;
|
||||
info.swap_successes = ip->swap_successes;
|
||||
return copy_to_user(up, &info, sizeof(info));
|
||||
}
|
||||
|
||||
static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in,
|
||||
int version)
|
||||
{
|
||||
if (version == IPC_64) {
|
||||
struct compat_shmid64_ds v;
|
||||
memset(&v, 0, sizeof(v));
|
||||
to_compat_ipc64_perm(&v.shm_perm, &in->shm_perm);
|
||||
v.shm_atime = in->shm_atime;
|
||||
v.shm_dtime = in->shm_dtime;
|
||||
v.shm_ctime = in->shm_ctime;
|
||||
v.shm_segsz = in->shm_segsz;
|
||||
v.shm_nattch = in->shm_nattch;
|
||||
v.shm_cpid = in->shm_cpid;
|
||||
v.shm_lpid = in->shm_lpid;
|
||||
return copy_to_user(buf, &v, sizeof(v));
|
||||
} else {
|
||||
struct compat_shmid_ds v;
|
||||
memset(&v, 0, sizeof(v));
|
||||
to_compat_ipc_perm(&v.shm_perm, &in->shm_perm);
|
||||
v.shm_perm.key = in->shm_perm.key;
|
||||
v.shm_atime = in->shm_atime;
|
||||
v.shm_dtime = in->shm_dtime;
|
||||
v.shm_ctime = in->shm_ctime;
|
||||
v.shm_segsz = in->shm_segsz;
|
||||
v.shm_nattch = in->shm_nattch;
|
||||
v.shm_cpid = in->shm_cpid;
|
||||
v.shm_lpid = in->shm_lpid;
|
||||
return copy_to_user(buf, &v, sizeof(v));
|
||||
}
|
||||
}
|
||||
|
||||
static int copy_compat_shmid_from_user(struct shmid64_ds *out, void __user *buf,
|
||||
int version)
|
||||
{
|
||||
memset(out, 0, sizeof(*out));
|
||||
if (version == IPC_64) {
|
||||
struct compat_shmid64_ds *p = buf;
|
||||
return get_compat_ipc64_perm(&out->shm_perm, &p->shm_perm);
|
||||
} else {
|
||||
struct compat_shmid_ds *p = buf;
|
||||
return get_compat_ipc_perm(&out->shm_perm, &p->shm_perm);
|
||||
}
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, void __user *, uptr)
|
||||
{
|
||||
struct ipc_namespace *ns;
|
||||
struct shmid64_ds sem64;
|
||||
int version = compat_ipc_parse_version(&cmd);
|
||||
int err;
|
||||
|
||||
ns = current->nsproxy->ipc_ns;
|
||||
|
||||
if (cmd < 0 || shmid < 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case IPC_INFO: {
|
||||
struct shminfo64 shminfo;
|
||||
err = shmctl_ipc_info(ns, &shminfo);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_compat_shminfo_to_user(uptr, &shminfo, version))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
case SHM_INFO: {
|
||||
struct shm_info shm_info;
|
||||
err = shmctl_shm_info(ns, &shm_info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (put_compat_shm_info(&shm_info, uptr))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
case IPC_STAT:
|
||||
case SHM_STAT:
|
||||
err = shmctl_stat(ns, shmid, cmd, &sem64);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_compat_shmid_to_user(&sem64, uptr, version))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
|
||||
case IPC_SET:
|
||||
if (copy_compat_shmid_from_user(&sem64, uptr, version))
|
||||
return -EFAULT;
|
||||
/* fallthru */
|
||||
case IPC_RMID:
|
||||
return shmctl_down(ns, shmid, cmd, &sem64);
|
||||
case SHM_LOCK:
|
||||
case SHM_UNLOCK:
|
||||
return shmctl_do_lock(ns, shmid, cmd);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
|
||||
|
@ -1267,6 +1441,25 @@ SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg)
|
|||
return (long)ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
#ifndef COMPAT_SHMLBA
|
||||
#define COMPAT_SHMLBA SHMLBA
|
||||
#endif
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg)
|
||||
{
|
||||
unsigned long ret;
|
||||
long err;
|
||||
|
||||
err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA);
|
||||
if (err)
|
||||
return err;
|
||||
force_successful_syscall_return();
|
||||
return (long)ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* detach and kill segment if marked destroyed.
|
||||
* The work is done in shm_close.
|
||||
|
@ -1397,7 +1590,7 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
|
|||
|
||||
seq_printf(s,
|
||||
"%10d %10d %4o " SIZE_SPEC " %5u %5u "
|
||||
"%5lu %5u %5u %5u %5u %10lu %10lu %10lu "
|
||||
"%5lu %5u %5u %5u %5u %10llu %10llu %10llu "
|
||||
SIZE_SPEC " " SIZE_SPEC "\n",
|
||||
shp->shm_perm.key,
|
||||
shp->shm_perm.id,
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
* the individual syscalls instead.
|
||||
*/
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_IPC
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ipc.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second,
|
||||
|
@ -97,3 +97,91 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second,
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#include <linux/compat.h>
|
||||
|
||||
#ifndef COMPAT_SHMLBA
|
||||
#define COMPAT_SHMLBA SHMLBA
|
||||
#endif
|
||||
|
||||
struct compat_ipc_kludge {
|
||||
compat_uptr_t msgp;
|
||||
compat_long_t msgtyp;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
|
||||
COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second,
|
||||
u32, third, compat_uptr_t, ptr, u32, fifth)
|
||||
{
|
||||
int version;
|
||||
u32 pad;
|
||||
|
||||
version = call >> 16; /* hack for backward compatibility */
|
||||
call &= 0xffff;
|
||||
|
||||
switch (call) {
|
||||
case SEMOP:
|
||||
/* struct sembuf is the same on 32 and 64bit :)) */
|
||||
return sys_semtimedop(first, compat_ptr(ptr), second, NULL);
|
||||
case SEMTIMEDOP:
|
||||
return compat_sys_semtimedop(first, compat_ptr(ptr), second,
|
||||
compat_ptr(fifth));
|
||||
case SEMGET:
|
||||
return sys_semget(first, second, third);
|
||||
case SEMCTL:
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
if (get_user(pad, (u32 __user *) compat_ptr(ptr)))
|
||||
return -EFAULT;
|
||||
return compat_sys_semctl(first, second, third, pad);
|
||||
|
||||
case MSGSND:
|
||||
return compat_sys_msgsnd(first, ptr, second, third);
|
||||
|
||||
case MSGRCV: {
|
||||
void __user *uptr = compat_ptr(ptr);
|
||||
|
||||
if (first < 0 || second < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!version) {
|
||||
struct compat_ipc_kludge ipck;
|
||||
if (!uptr)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ipck, uptr, sizeof(ipck)))
|
||||
return -EFAULT;
|
||||
return compat_sys_msgrcv(first, ipck.msgp, second,
|
||||
ipck.msgtyp, third);
|
||||
}
|
||||
return compat_sys_msgrcv(first, ptr, second, fifth, third);
|
||||
}
|
||||
case MSGGET:
|
||||
return sys_msgget(first, second);
|
||||
case MSGCTL:
|
||||
return compat_sys_msgctl(first, second, compat_ptr(ptr));
|
||||
|
||||
case SHMAT: {
|
||||
int err;
|
||||
unsigned long raddr;
|
||||
|
||||
if (version == 1)
|
||||
return -EINVAL;
|
||||
err = do_shmat(first, compat_ptr(ptr), second, &raddr,
|
||||
COMPAT_SHMLBA);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return put_user(raddr, (compat_ulong_t *)compat_ptr(third));
|
||||
}
|
||||
case SHMDT:
|
||||
return sys_shmdt(compat_ptr(ptr));
|
||||
case SHMGET:
|
||||
return sys_shmget(first, (unsigned)second, third);
|
||||
case SHMCTL:
|
||||
return compat_sys_shmctl(first, second, compat_ptr(ptr));
|
||||
}
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
30
ipc/util.h
30
ipc/util.h
|
@ -194,4 +194,34 @@ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
|
|||
const struct ipc_ops *ops, struct ipc_params *params);
|
||||
void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
|
||||
void (*free)(struct ipc_namespace *, struct kern_ipc_perm *));
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#include <linux/compat.h>
|
||||
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;
|
||||
};
|
||||
|
||||
void to_compat_ipc_perm(struct compat_ipc_perm *, struct ipc64_perm *);
|
||||
void to_compat_ipc64_perm(struct compat_ipc64_perm *, struct ipc64_perm *);
|
||||
int get_compat_ipc_perm(struct ipc64_perm *, struct compat_ipc_perm __user *);
|
||||
int get_compat_ipc64_perm(struct ipc64_perm *,
|
||||
struct compat_ipc64_perm __user *);
|
||||
|
||||
static inline int compat_ipc_parse_version(int *cmd)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION
|
||||
int version = *cmd & IPC_64;
|
||||
*cmd &= ~IPC_64;
|
||||
return version;
|
||||
#else
|
||||
return IPC_64;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -182,7 +182,7 @@ struct audit_context {
|
|||
mqd_t mqdes;
|
||||
size_t msg_len;
|
||||
unsigned int msg_prio;
|
||||
struct timespec abs_timeout;
|
||||
struct timespec64 abs_timeout;
|
||||
} mq_sendrecv;
|
||||
struct {
|
||||
int oflag;
|
||||
|
|
|
@ -1235,11 +1235,11 @@ static void show_special(struct audit_context *context, int *call_panic)
|
|||
case AUDIT_MQ_SENDRECV:
|
||||
audit_log_format(ab,
|
||||
"mqdes=%d msg_len=%zd msg_prio=%u "
|
||||
"abs_timeout_sec=%ld abs_timeout_nsec=%ld",
|
||||
"abs_timeout_sec=%lld abs_timeout_nsec=%ld",
|
||||
context->mq_sendrecv.mqdes,
|
||||
context->mq_sendrecv.msg_len,
|
||||
context->mq_sendrecv.msg_prio,
|
||||
context->mq_sendrecv.abs_timeout.tv_sec,
|
||||
(long long) context->mq_sendrecv.abs_timeout.tv_sec,
|
||||
context->mq_sendrecv.abs_timeout.tv_nsec);
|
||||
break;
|
||||
case AUDIT_MQ_NOTIFY:
|
||||
|
@ -2083,15 +2083,15 @@ void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr)
|
|||
*
|
||||
*/
|
||||
void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
|
||||
const struct timespec *abs_timeout)
|
||||
const struct timespec64 *abs_timeout)
|
||||
{
|
||||
struct audit_context *context = current->audit_context;
|
||||
struct timespec *p = &context->mq_sendrecv.abs_timeout;
|
||||
struct timespec64 *p = &context->mq_sendrecv.abs_timeout;
|
||||
|
||||
if (abs_timeout)
|
||||
memcpy(p, abs_timeout, sizeof(struct timespec));
|
||||
memcpy(p, abs_timeout, sizeof(*p));
|
||||
else
|
||||
memset(p, 0, sizeof(struct timespec));
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
context->mq_sendrecv.mqdes = mqdes;
|
||||
context->mq_sendrecv.msg_len = msg_len;
|
||||
|
|
|
@ -200,29 +200,6 @@ int compat_put_timespec(const struct timespec *ts, void __user *uts)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(compat_put_timespec);
|
||||
|
||||
int compat_convert_timespec(struct timespec __user **kts,
|
||||
const void __user *cts)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timespec __user *uts;
|
||||
|
||||
if (!cts || COMPAT_USE_64BIT_TIME) {
|
||||
*kts = (struct timespec __user *)cts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uts = compat_alloc_user_space(sizeof(ts));
|
||||
if (!uts)
|
||||
return -EFAULT;
|
||||
if (compat_get_timespec(&ts, cts))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(uts, &ts, sizeof(ts)))
|
||||
return -EFAULT;
|
||||
|
||||
*kts = uts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_compat_itimerval(struct itimerval *o, const struct compat_itimerval __user *i)
|
||||
{
|
||||
struct compat_itimerval v32;
|
||||
|
|
Loading…
Reference in New Issue