As the 32-bit architectures move these system calls over to 64-bit time_t, we have to provide backwards-compatibility support.
We could use the existing compat_sys_mq_timed{send,receive} functions from ipc/compat_mq.c, but their use of compat_convert_timespec() means that we would have to implement compat_alloc_userspace() for all architectures, which is rather tricky.
As an alternative approach, this splits up the normal sys_mq_timed{send,receive} functions into the main part and the actual system call entry, and then reuses the main part from the compat handler, which has the added benefit of being slightly more efficent for compat than the existing approach, as we avoid copying the time data to the kernel twice.
We could probably share a little mode code here, but this patch for now avoids doing too many changes at once.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat_time.h | 2 +- ipc/compat_mq.c | 28 ---------- ipc/mqueue.c | 128 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 101 insertions(+), 57 deletions(-)
diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index 6d129a7f3843..37c3860d97fa 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -285,7 +285,7 @@ asmlinkage long compat_sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr, compat_size_t msg_len, unsigned int msg_prio, const struct compat_timespec __user *u_abs_timeout); -asmlinkage ssize_t compat_sys_mq_timedreceive(mqd_t mqdes, +asmlinkage long compat_sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, compat_size_t msg_len, unsigned int __user *u_msg_prio, const struct compat_timespec __user *u_abs_timeout); diff --git a/ipc/compat_mq.c b/ipc/compat_mq.c index ef6f91cc4490..19d3c3f3ab0d 100644 --- a/ipc/compat_mq.c +++ b/ipc/compat_mq.c @@ -64,34 +64,6 @@ COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name, return sys_mq_open(u_name, oflag, mode, p); }
-COMPAT_SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, - const char __user *, u_msg_ptr, - compat_size_t, msg_len, unsigned int, msg_prio, - const struct compat_timespec __user *, u_abs_timeout) -{ - struct timespec __user *u_ts; - - if (compat_convert_timespec(&u_ts, u_abs_timeout)) - return -EFAULT; - - return sys_mq_timedsend(mqdes, u_msg_ptr, msg_len, - msg_prio, u_ts); -} - -COMPAT_SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, - char __user *, u_msg_ptr, - compat_size_t, msg_len, unsigned int __user *, u_msg_prio, - const struct compat_timespec __user *, u_abs_timeout) -{ - struct timespec __user *u_ts; - - if (compat_convert_timespec(&u_ts, u_abs_timeout)) - return -EFAULT; - - return sys_mq_timedreceive(mqdes, u_msg_ptr, msg_len, - u_msg_prio, u_ts); -} - COMPAT_SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes, const struct compat_sigevent __user *, u_notification) { diff --git a/ipc/mqueue.c b/ipc/mqueue.c index f3f27ad1ca44..0fd4d5b9a959 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -677,7 +677,19 @@ static int prepare_timeout(const struct __kernel_timespec __user *u_abs_timeout, { if (get_timespec64(ts, u_abs_timeout)) return -EFAULT; - if (!timespec_valid(ts)) + if (!timespec64_valid(ts)) + return -EINVAL; + + *expires = timespec64_to_ktime(*ts); + return 0; +} + +static int compat_prepare_timeout(const struct compat_timespec __user *u_abs_timeout, + ktime_t *expires, struct timespec64 *ts) +{ + if (compat_get_timespec64(ts, u_abs_timeout)) + return -EFAULT; + if (!timespec64_valid(ts)) return -EINVAL;
*expires = timespec64_to_ktime(*ts); @@ -951,9 +963,9 @@ static inline void pipelined_receive(struct mqueue_inode_info *info) sender->state = STATE_READY; }
-SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, - size_t, msg_len, unsigned int, msg_prio, - const struct __kernel_timespec __user *, u_abs_timeout) +static long mq_timedsend(mqd_t mqdes, const char __user * u_msg_ptr, + size_t msg_len, unsigned int msg_prio, + ktime_t *timeout) { struct fd f; struct inode *inode; @@ -961,23 +973,9 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, struct ext_wait_queue *receiver; struct msg_msg *msg_ptr; struct mqueue_inode_info *info; - ktime_t expires, *timeout = NULL; - struct timespec64 ts; struct posix_msg_tree_node *new_leaf = NULL; int ret = 0;
- if (u_abs_timeout) { - int res = prepare_timeout(u_abs_timeout, &expires, &ts); - if (res) - return res; - timeout = &expires; - } - - if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) - return -EINVAL; - - audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL); - f = fdget(mqdes); if (unlikely(!f.file)) { ret = -EBADF; @@ -1071,19 +1069,12 @@ out: return ret; }
-SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, - size_t, msg_len, unsigned int __user *, u_msg_prio, +SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, + size_t, msg_len, unsigned int, msg_prio, const struct __kernel_timespec __user *, u_abs_timeout) { - ssize_t ret; - struct msg_msg *msg_ptr; - struct fd f; - struct inode *inode; - struct mqueue_inode_info *info; - struct ext_wait_queue wait; ktime_t expires, *timeout = NULL; struct timespec64 ts; - struct posix_msg_tree_node *new_leaf = NULL;
if (u_abs_timeout) { int res = prepare_timeout(u_abs_timeout, &expires, &ts); @@ -1092,7 +1083,49 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, timeout = &expires; }
- audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL); + if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) + return -EINVAL; + + audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL); + + return mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, timeout); +} + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, + compat_size_t, msg_len, unsigned int, msg_prio, + const struct compat_timespec __user *, u_abs_timeout) +{ + ktime_t expires, *timeout = NULL; + struct timespec64 ts; + + if (u_abs_timeout) { + int res = compat_prepare_timeout(u_abs_timeout, &expires, &ts); + if (res) + return res; + timeout = &expires; + } + + if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) + return -EINVAL; + + audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL); + + return mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, timeout); +} +#endif + +static int mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, + size_t msg_len, unsigned int __user *u_msg_prio, + ktime_t *timeout) +{ + ssize_t ret; + struct msg_msg *msg_ptr; + struct fd f; + struct inode *inode; + struct mqueue_inode_info *info; + struct ext_wait_queue wait; + struct posix_msg_tree_node *new_leaf = NULL;
f = fdget(mqdes); if (unlikely(!f.file)) { @@ -1174,6 +1207,45 @@ out: return ret; }
+SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, + size_t, msg_len, unsigned int __user *, u_msg_prio, + const struct __kernel_timespec __user *, u_abs_timeout) +{ + ktime_t expires, *timeout = NULL; + struct timespec64 ts; + + if (u_abs_timeout) { + int res = prepare_timeout(u_abs_timeout, &expires, &ts); + if (res) + return res; + timeout = &expires; + } + + audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL); + + return mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, timeout); +} + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, + compat_size_t, msg_len, unsigned int __user *, u_msg_prio, + const struct compat_timespec __user *, u_abs_timeout) +{ + ktime_t expires, *timeout = NULL; + struct timespec64 ts; + + if (u_abs_timeout) { + int res = compat_prepare_timeout(u_abs_timeout, &expires, &ts); + if (res) + return res; + timeout = &expires; + } + + audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL); + + return mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, timeout); +} +#endif /* * Notes: the case when user wants us to deregister (with NULL as pointer) * and he isn't currently owner of notification, will be silently discarded.