[PATCH v3 5/5]Added bitmask flag and user struct argument size as input for extensibility
Mathura_Kumar <[email protected]> Wed, 8 Apr 2026 15:21:33 +0530
| Newsgroups | dev.linux.lists.criu |
|---|---|
| Message-ID | <[email protected]> |
Signed-off-by: Mathura_Kumar <[email protected]> --- Documentation/userspace-api/ipc.rst | 24 ++-- include/linux/compat.h | 4 +- include/linux/syscalls.h | 4 +- ipc/mqueue.c | 179 +++++++++++++++----------- kernel/sys_ni.c | 2 + tools/testing/selftests/ipc/mq_peek.c | 54 ++++++-- 6 files changed, 163 insertions(+), 104 deletions(-) diff --git a/Documentation/userspace-api/ipc.rst b/Documentation/userspace-api/ipc.rst index 14a5f0ce7230..3fc46621b5c6 100644 --- a/Documentation/userspace-api/ipc.rst +++ b/Documentation/userspace-api/ipc.rst @@ -54,13 +54,10 @@ SYNOPSIS ssize_t mq_timedreceive2(mqd_t mqdes, struct mq_timedreceive2_args *uargs, - unsigned int flags, + size_t usize, unsigned int flags, unsigned long index, const struct timespec *abs_timeout); -Note: No glibc wrapper exists for this syscall. Callers must invoke it -directly using syscall(2). - DESCRIPTION mq_timedreceive2() receives or peeks at a message from the message queue referred to by the descriptor mqdes. @@ -78,14 +75,19 @@ DESCRIPTION If not NULL, the priority of the received message is stored here. + The usize argument take size of struct to make mq_timedreceive2_args + extensible in future if required. + The flags argument controls receive behavior. The following flag is defined: ``MQ_PEEK`` Copy the message into msg_ptr without removing it from - the queue. The queue is not modified. If this flag is - not set, behavior is identical to mq_timedreceive() and - the message is consumed. + the queue. The queue is not modified. + + ``MQ_RECV`` + Copy the message into msg_ptr and consume a single message by + invoking the existing do_mq_timedreceive() handler. The index argument selects which message to operate on within the priority-ordered queue. index 0 refers to the highest @@ -116,7 +118,7 @@ ERRORS ``EINVAL`` flags contains an unknown value, or index is nonzero - and MQ_PEEK is not set. + and contradictory flag was passed. ``EMSGSIZE`` msg_len is less than the mq_msgsize attribute of @@ -137,7 +139,7 @@ consolidated into struct mq_timedreceive2_args rather than passed as individual syscall arguments. Due to limited six arguments, The original mq_timedreceive() consumes all six slots, leaving no room for extension. Consolidating the buffer parameters -into a struct recovers two argument slots for flags and index while +into a struct recovers three argument slots for flags, size and index while keeping the interface clean and forward-compatible. Future extensions can be made by adding new flag bits without @@ -220,3 +222,7 @@ Tests for mq_timedreceive2() should cover the following: threads returns consistent results and does not corrupt queue state. Verify that a concurrent mq_receive() and mq_peek() do not race. + +9) Contradictory Operation: verify that simultaneous inconsistent flag work as + expected. + diff --git a/include/linux/compat.h b/include/linux/compat.h index 9f5ca26e76d8..837a59559fa5 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -803,8 +803,8 @@ asmlinkage long compat_sys_pwritev64v2(unsigned long fd, unsigned long vlen, loff_t pos, rwf_t flags); #endif asmlinkage long compat_sys_mq_timedreceive2(mqd_t mqdes, struct compat_mq_timedreceive2_args __user *uargs, - unsigned int flags, unsigned long index, - struct old_timespec32 __user *abs_timeout); + size_t usize, unsigned int flags, const unsigned long index, + const struct old_timespec32 __user *abs_timeout); /* * Deprecated system calls which are still defined in * include/uapi/asm-generic/unistd.h and wanted by >= 1 arch diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 993e570c90ab..e5b544464a44 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -750,8 +750,8 @@ asmlinkage long sys_mq_timedsend_time32(mqd_t mqdes, const struct old_timespec32 __user *u_abs_timeout); asmlinkage long sys_mq_timedreceive2(mqd_t mqdes, struct mq_timedreceive2_args __user *uargs, - unsigned int flags, unsigned long index, - struct __kernel_timespec __user *abs_timeout); + size_t usize, unsigned int flags, const unsigned long index, + const struct __kernel_timespec __user *abs_timeout); asmlinkage long sys_msgget(key_t key, int msgflg); asmlinkage long sys_old_msgctl(int msqid, int cmd, struct msqid_ds __user *buf); asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 78dc414967a2..2d4c285e4aa1 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -38,6 +38,7 @@ #include <linux/sched/wake_q.h> #include <linux/sched/signal.h> #include <linux/sched/user.h> +#include <linux/uaccess.h> #include <net/sock.h> #include "util.h" @@ -53,7 +54,10 @@ struct mqueue_fs_context { #define SEND 0 #define RECV 1 -#define MQ_PEEK 2 + +#define MQ_PEEK 0x02 +#define MQ_RECV 0x04 +#define MQ_VALID_FLAGS (MQ_PEEK | MQ_RECV) #define STATE_NONE 0 #define STATE_READY 1 @@ -1266,78 +1270,85 @@ static int do_mq_timedreceive2(mqd_t mqdes, struct mq_timedreceive2_args *args, struct inode *inode; struct mqueue_inode_info *info; - if (!(flags & MQ_PEEK)) { - return do_mq_timedreceive(mqdes, args->msg_ptr, args->msg_len, - args->msg_prio, ts); - } - audit_mq_sendrecv(mqdes, args->msg_len, 0, ts); - CLASS(fd, f)(mqdes); - if (fd_empty(f)) - return -EBADF; - - inode = file_inode(fd_file(f)); - if (unlikely(fd_file(f)->f_op != &mqueue_file_operations)) - return -EBADF; - info = MQUEUE_I(inode); - audit_file(fd_file(f)); + if (flags & (~MQ_VALID_FLAGS)) + return -EINVAL; + if ((flags & MQ_RECV) && (flags & MQ_PEEK)) + return -EINVAL; + if (flags & MQ_PEEK) { + audit_mq_sendrecv(mqdes, args->msg_len, 0, ts); + CLASS(fd, f)(mqdes); + if (fd_empty(f)) + return -EBADF; + + inode = file_inode(fd_file(f)); + if (unlikely(fd_file(f)->f_op != &mqueue_file_operations)) + return -EBADF; + info = MQUEUE_I(inode); + audit_file(fd_file(f)); - if (unlikely(!(fd_file(f)->f_mode & FMODE_READ))) - return -EBADF; + if (unlikely(!(fd_file(f)->f_mode & FMODE_READ))) + return -EBADF; - if (unlikely(args->msg_len < info->attr.mq_msgsize)) - return -EMSGSIZE; - if (index >= (unsigned long)info->attr.mq_maxmsg) - return -ENOENT; + if (unlikely(args->msg_len < info->attr.mq_msgsize)) + return -EMSGSIZE; + if (index >= (unsigned long)info->attr.mq_maxmsg) + return -ENOENT; - spin_lock(&info->lock); - if (info->attr.mq_curmsgs == 0) { - spin_unlock(&info->lock); - return -EAGAIN; - } - msg_ptr = mq_peek_index(info, index); - if (!msg_ptr) { + spin_lock(&info->lock); + if (info->attr.mq_curmsgs == 0) { + spin_unlock(&info->lock); + return -EAGAIN; + } + msg_ptr = mq_peek_index(info, index); + if (!msg_ptr) { + spin_unlock(&info->lock); + return -ENOENT; + } + k_m_type = msg_ptr->m_type; + k_m_ts = msg_ptr->m_ts; spin_unlock(&info->lock); - return -ENOENT; - } - k_m_type = msg_ptr->m_type; - k_m_ts = msg_ptr->m_ts; - spin_unlock(&info->lock); - k_msg_buffer = alloc_msg(k_m_ts); - if (!k_msg_buffer) - return -ENOMEM; + k_msg_buffer = alloc_msg(k_m_ts); + if (!k_msg_buffer) + return -ENOMEM; /* * Two spin locks are necessary here. We are avoiding atomic memory * allocation and premature allocation before confirming that * a message actually exists to peek. */ - spin_lock(&info->lock); - msg_ptr = mq_peek_index(info, index); - if (!msg_ptr || msg_ptr->m_type != k_m_type || - msg_ptr->m_ts != k_m_ts) { - spin_unlock(&info->lock); - free_msg(k_msg_buffer); - return -EAGAIN; - } - if (IS_ERR(copy_msg(msg_ptr, k_msg_buffer, k_m_ts))) { + spin_lock(&info->lock); + msg_ptr = mq_peek_index(info, index); + if (!msg_ptr || msg_ptr->m_type != k_m_type || + msg_ptr->m_ts != k_m_ts) { + spin_unlock(&info->lock); + free_msg(k_msg_buffer); + return -EAGAIN; + } + if (IS_ERR(copy_msg(msg_ptr, k_msg_buffer, k_m_ts))) { + spin_unlock(&info->lock); + free_msg(k_msg_buffer); + return -EINVAL; + } spin_unlock(&info->lock); - free_msg(k_msg_buffer); - return -EINVAL; - } - spin_unlock(&info->lock); - ret = k_msg_buffer->m_ts; - if (args->msg_prio && put_user(k_m_type, args->msg_prio)) { + ret = k_msg_buffer->m_ts; + if (args->msg_prio && put_user(k_m_type, args->msg_prio)) { + free_msg(k_msg_buffer); + return -EFAULT; + } + if (store_msg(args->msg_ptr, k_msg_buffer, k_m_ts)) { + free_msg(k_msg_buffer); + return -EFAULT; + } free_msg(k_msg_buffer); - return -EFAULT; + return ret; } - if (store_msg(args->msg_ptr, k_msg_buffer, k_m_ts)) { - free_msg(k_msg_buffer); - return -EFAULT; + if (flags & MQ_RECV) { + return do_mq_timedreceive(mqdes, args->msg_ptr, args->msg_len, args->msg_prio, ts); } - free_msg(k_msg_buffer); - return ret; + + return -EINVAL; } SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, @@ -1368,17 +1379,22 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, p); } -SYSCALL_DEFINE5(mq_timedreceive2, mqd_t, mqdes, - struct mq_timedreceive2_args __user *, uargs, unsigned int, - flags, const unsigned long, index, - const struct __kernel_timespec __user *, u_abs_timeout) +SYSCALL_DEFINE6(mq_timedreceive2, mqd_t, mqdes, + struct mq_timedreceive2_args __user *, uargs, size_t, usize, + unsigned int, flags, const unsigned long, index, + const struct __kernel_timespec __user *, u_abs_timeout) { - struct mq_timedreceive2_args args; + struct mq_timedreceive2_args k_args; struct timespec64 ts, *p = NULL; + int err; - if (copy_from_user(&args, uargs, sizeof(args))) - return -EFAULT; - + if (unlikely(usize < sizeof(struct mq_timedreceive2_args))) + return -EINVAL; + if (unlikely(usize > PAGE_SIZE)) + return -E2BIG; + err = copy_struct_from_user(&k_args, sizeof(k_args), uargs, usize); + if (err) + return err; if (u_abs_timeout) { int res = prepare_timeout(u_abs_timeout, &ts); @@ -1386,7 +1402,7 @@ SYSCALL_DEFINE5(mq_timedreceive2, mqd_t, mqdes, return res; p = &ts; } - return do_mq_timedreceive2(mqdes, &args, flags, index, p); + return do_mq_timedreceive2(mqdes, &k_args, flags, index, p); } /* @@ -1633,12 +1649,18 @@ static inline int put_compat_mq_attr(const struct mq_attr *attr, } static inline int get_compat_mq_args(struct mq_timedreceive2_args *args, - struct compat_mq_timedreceive2_args __user *uargs) + struct compat_mq_timedreceive2_args __user *uargs, size_t usize) { struct compat_mq_timedreceive2_args v; + int err; - if (copy_from_user(&v, uargs, sizeof(v))) - return -EFAULT; + if (unlikely(usize < sizeof(struct compat_mq_timedreceive2_args))) + return -EINVAL; + if (unlikely(usize > PAGE_SIZE)) + return -E2BIG; + err = copy_struct_from_user(&v, sizeof(v), uargs, usize); + if (err) + return err; memset(args, 0, sizeof(*args)); args->msg_len = (size_t)v.msg_len; @@ -1700,17 +1722,18 @@ COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes, return 0; } -COMPAT_SYSCALL_DEFINE5(mq_timedreceive2, mqd_t, mqdes, - struct compat_mq_timedreceive2_args __user *, uargs, - unsigned int, flags, const unsigned long, index, - const struct old_timespec32 __user *, u_abs_timeout) +COMPAT_SYSCALL_DEFINE6(mq_timedreceive2, mqd_t, mqdes, + struct compat_mq_timedreceive2_args __user *, uargs, size_t, usize, + unsigned int, flags, const unsigned long, index, + const struct old_timespec32 __user *, u_abs_timeout) { - struct mq_timedreceive2_args args; + struct mq_timedreceive2_args k_args; struct timespec64 ts, *p = NULL; + int err; - if (get_compat_mq_args(&args, uargs)) - return -EFAULT; - + err = get_compat_mq_args(&k_args, uargs, usize); + if (err) + return err; if (u_abs_timeout) { int res = compat_prepare_timeout(u_abs_timeout, &ts); @@ -1718,7 +1741,7 @@ COMPAT_SYSCALL_DEFINE5(mq_timedreceive2, mqd_t, mqdes, return res; p = &ts; } - return do_mq_timedreceive2(mqdes, &args, flags, index, p); + return do_mq_timedreceive2(mqdes, &k_args, flags, index, p); } #endif diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 658d6b8274b3..af956bc4e6fe 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -391,6 +391,8 @@ COND_SYSCALL(setuid16); /* restartable sequence */ COND_SYSCALL(rseq); COND_SYSCALL(rseq_slice_yield); + +/* ipc */ COND_SYSCALL(mq_timedreceive2); COND_SYSCALL(uretprobe); diff --git a/tools/testing/selftests/ipc/mq_peek.c b/tools/testing/selftests/ipc/mq_peek.c index d08596ae6e1c..0ec3f2fca82c 100644 --- a/tools/testing/selftests/ipc/mq_peek.c +++ b/tools/testing/selftests/ipc/mq_peek.c @@ -40,7 +40,8 @@ #endif #endif -#define MQ_PEEK 2U +#define MQ_PEEK 0x02 +#define MQ_RECV 0x04 #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) @@ -52,11 +53,13 @@ struct mq_timedreceive2_args { char *msg_ptr; }; -static long mq_timedreceive2(mqd_t mqdes, struct mq_timedreceive2_args *uargs, - unsigned int flags, unsigned long index, +#define u_struct_size sizeof(struct mq_timedreceive2_args) + +static long mq_timedreceive2(mqd_t mqdes, struct mq_timedreceive2_args *uargs, size_t usize, + unsigned int flags, const unsigned long index, const struct timespec *timeout) { - return syscall(__NR_mq_timedreceive2, (long)mqdes, uargs, (long)flags, + return syscall(__NR_mq_timedreceive2, mqdes, uargs, usize, flags, index, timeout); } @@ -109,7 +112,7 @@ static long peek(mqd_t mqd, unsigned long index, char *buf, size_t bufsz, .msg_prio = prio, .msg_ptr = buf, }; - return mq_timedreceive2(mqd, &args, MQ_PEEK, index, NULL); + return mq_timedreceive2(mqd, &args, u_struct_size, MQ_PEEK, index, NULL); } static long queue_depth(mqd_t mqd) @@ -226,7 +229,7 @@ static void test_peek_buffer_too_small(void) send_msg(mqd, PRIO_HIGH, "hello", 5); - ret = mq_timedreceive2(mqd, &args, MQ_PEEK, 0, NULL); + ret = mq_timedreceive2(mqd, &args, u_struct_size, MQ_PEEK, 0, NULL); if (ret == -1 && errno == EMSGSIZE) ksft_test_result_pass("peek with small buf [EMSGSIZE]\n"); else @@ -246,11 +249,10 @@ static void test_peek_bad_msg_ptr(void) .msg_prio = &prio, .msg_ptr = (char *)0x1, }; - long ret; send_msg(mqd, PRIO_HIGH, "payload", 7); - ret = mq_timedreceive2(mqd, &args, MQ_PEEK, 0, NULL); + ret = mq_timedreceive2(mqd, &args, u_struct_size, MQ_PEEK, 0, NULL); if (ret == -1 && errno == EFAULT) ksft_test_result_pass("peek bad msg_ptr [EFAULT]\n"); else @@ -588,7 +590,7 @@ static void test_no_peek_flag_is_receive(void) send_msg(mqd, PRIO_HIGH, "consume-me", 10); - ret = mq_timedreceive2(mqd, &args, 0, 0, NULL); + ret = mq_timedreceive2(mqd, &args, u_struct_size, MQ_RECV, 0, NULL); if (ret < 0) { ksft_test_result_fail("no-peek receive failed: ret=%ld errno=%d\n", ret, errno); mq_close(mqd); @@ -615,8 +617,7 @@ static void *receiver_thread(void *arg) ssize_t r; while ((r = mq_receive(ctx->mqd, buf, sizeof(buf), &prio)) > 0) - ; - + ; return NULL; } @@ -680,7 +681,7 @@ static void test_peek_null_prio_ptr(void) send_msg(mqd, PRIO_MED, "no-prio-needed", 14); - ret = mq_timedreceive2(mqd, &args, MQ_PEEK, 0, NULL); + ret = mq_timedreceive2(mqd, &args, u_struct_size, MQ_PEEK, 0, NULL); if (ret >= 0) ksft_test_result_pass("peek with NULL msg_prio ptr: OK\n"); else @@ -734,6 +735,32 @@ static void test_peek_priority_matches_receive(void) mq_close(mqd); } +static void test_contradictory_operation(void) +{ + mqd_t mqd = open_queue("contra_op", MAX_MSG_SIZE); + char buf[MAX_MSG_SIZE]; + struct mq_timedreceive2_args args = { + .msg_len = sizeof(buf), + .msg_prio = NULL, + .msg_ptr = buf, + }; + long ret; + + send_msg(mqd, PRIO_HIGH, "A", 1); + send_msg(mqd, PRIO_MED, "B", 1); + send_msg(mqd, PRIO_LOW, "C", 1); + + ret = mq_timedreceive2(mqd, &args, u_struct_size, MQ_PEEK | MQ_RECV, 0, 0); + + if (ret == -1 && errno == EINVAL) + ksft_test_result_pass("Handler rejected invalid/contradictory request [EINVAL]\n"); + else + ksft_test_result_fail("Handler did not reject invalid request: ret=%ld errno=%d (%m)\n", + ret, errno); + + mq_close(mqd); +} + static const struct { const char *name; void (*fn)(void); @@ -756,6 +783,7 @@ static const struct { { "NULL msg_prio ptr", test_peek_null_prio_ptr }, { "peeked prio matches mq_receive", test_peek_priority_matches_receive }, + {"two_contradictory_operation_togther", test_contradictory_operation} }; int main(void) @@ -769,7 +797,7 @@ int main(void) { struct mq_timedreceive2_args probe_args = { 0 }; - sc_ret = mq_timedreceive2((mqd_t)-1, &probe_args, MQ_PEEK, 0, + sc_ret = mq_timedreceive2((mqd_t)-1, &probe_args, u_struct_size, MQ_PEEK, 0, NULL); if (sc_ret == -1 && errno == ENOSYS) ksft_exit_skip("mq_timedreceive2 syscall not available (NR=%d ENOSYS) — is the kernel too old?\n", -- 2.43.0