Commit cc73fee0 authored by Linus Torvalds's avatar Linus Torvalds

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
parents e7cdb60f aaed2dd8
......@@ -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
......@@ -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, &times->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 @@ static int utimes_common(const struct path *path, struct timespec *times)
* 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 @@ long do_utimes(int dfd, const char __user *filename, struct timespec *times,
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(&times, 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;
......
......@@ -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)
{
#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)))
return -EFAULT;
return __get_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm);
}
static inline int get_compat_semid_ds(struct semid64_ds *s,
struct compat_semid_ds __user *up)
{
if (!access_ok(VERIFY_READ, up, sizeof(*up)))
return -EFAULT;
return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm);
}
static inline int put_compat_semid64_ds(struct semid64_ds *sem64,
struct compat_semid64_ds __user *up64)
{
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;
}
static inline int put_compat_semid_ds(struct semid64_ds *s,
struct compat_semid_ds __user *up)
{
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 get_compat_ipc64_perm(struct ipc64_perm *to,
struct compat_ipc64_perm __user *from)
{
int err;
if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
struct compat_ipc64_perm v;
if (copy_from_user(&v, from, sizeof(v)))
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;
to->uid = v.uid;
to->gid = v.gid;
to->mode = v.mode;
return 0;
}
static inline int put_compat_shm_info(struct shm_info __user *ip,
struct compat_shm_info __user *uip)
int get_compat_ipc_perm(struct ipc64_perm *to,
struct compat_ipc_perm __user *from)
{
int err;
struct shm_info si;
if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip)) ||
copy_from_user(&si, ip, sizeof(si)))
struct compat_ipc_perm v;
if (copy_from_user(&v, from, sizeof(v)))
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;
to->uid = v.uid;
to->gid = v.gid;
to->mode = v.mode;
return 0;
}
COMPAT_SYSCALL_DEFINE3(shmctl, int, first, int, second, void __user *, uptr)
void to_compat_ipc64_perm(struct compat_ipc64_perm *to, struct ipc64_perm *from)
{
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;
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;
}
COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems,
unsigned, nsops,
const struct compat_timespec __user *, timeout)
void to_compat_ipc_perm(struct compat_ipc_perm *to, struct ipc64_perm *from)
{
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;
}
......@@ -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 @@ static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
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)
......
......@@ -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,22 +436,12 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
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;
struct msg_queue *msq;
switch (cmd) {
case IPC_INFO:
case MSG_INFO:
{
struct msginfo msginfo;
int max_id;
if (!buf)
return -EFAULT;
/*
* We must not return kernel stack data.
* due to padding, it's not enough
......@@ -467,39 +451,35 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid,
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;
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);
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;
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;
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;
if (!buf)
return -EFAULT;
memset(&tbuf, 0, sizeof(tbuf));
memset(p, 0, sizeof(*p));
rcu_read_lock();
if (cmd == MSG_STAT) {
......@@ -526,27 +506,19 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid,
if (err)
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;
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();
if (copy_msqid_to_user(buf, &tbuf, version))
return -EFAULT;
return success_return;
}
default:
return -EINVAL;
}
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,
......
......@@ -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,53 +1178,14 @@ 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;
switch (cmd) {
case IPC_INFO:
case SEM_INFO:
{
struct seminfo seminfo;
int max_id;
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;
int err;
memset(&tbuf, 0, sizeof(tbuf));
memset(semid64, 0, sizeof(*semid64));
rcu_read_lock();
if (cmd == SEM_STAT) {
......@@ -1250,40 +1211,62 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
if (err)
goto out_unlock;
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;
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();
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,25 +1581,136 @@ 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:
case GETNCNT:
case GETZCNT:
case SETALL:
return semctl_main(ns, semid, semnum, cmd, p);
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:
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_RMID:
case IPC_SET:
return semctl_down(ns, semid, cmd, version, p);
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,
......@@ -1766,8 +1856,8 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
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 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
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,
......
......@@ -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,67 +864,51 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
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;
if (err < 0)
err = 0;
}
return err;
}
err = err < 0 ? 0 : err;
goto out;
}
case SHM_STAT:
case IPC_STAT:
{
struct shmid64_ds tbuf;
static int shmctl_stat(struct ipc_namespace *ns, int shmid,
int cmd, struct shmid64_ds *tbuf)
{
struct shmid_kernel *shp;
int result;
int err;
rcu_read_lock();
if (cmd == SHM_STAT) {
......@@ -957,58 +935,28 @@ static int shmctl_nolock(struct ipc_namespace *ns, int shmid,
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;
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;
}
return result;
out_unlock:
rcu_read_unlock();
out:
return err;
}
SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd)
{
struct shmid_kernel *shp;
int err, version;
struct ipc_namespace *ns;
if (cmd < 0 || shmid < 0)
return -EINVAL;
version = ipc_parse_version(&cmd);
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;
int err;
rcu_read_lock();
shp = shm_obtain_object_check(ns, shmid);
......@@ -1072,10 +1020,6 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
fput(shm_file);
return err;
}
default:
return -EINVAL;
}
out_unlock0:
ipc_unlock_object(&shp->shm_perm);
......@@ -1084,6 +1028,236 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
return err;
}
SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
{
int err, version;
struct ipc_namespace *ns;
struct shmid64_ds sem64;
if (cmd < 0 || shmid < 0)
return -EINVAL;
version = ipc_parse_version(&cmd);
ns = current->nsproxy->ipc_ns;
switch (cmd) {
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;
}
}
#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
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment