mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
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:
@@ -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
File diff suppressed because it is too large
Load Diff
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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user