162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/ipc/msgutil.c 462306a36Sopenharmony_ci * Copyright (C) 1999, 2004 Manfred Spraul 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/spinlock.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/security.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/ipc.h> 1262306a36Sopenharmony_ci#include <linux/msg.h> 1362306a36Sopenharmony_ci#include <linux/ipc_namespace.h> 1462306a36Sopenharmony_ci#include <linux/utsname.h> 1562306a36Sopenharmony_ci#include <linux/proc_ns.h> 1662306a36Sopenharmony_ci#include <linux/uaccess.h> 1762306a36Sopenharmony_ci#include <linux/sched.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "util.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciDEFINE_SPINLOCK(mq_lock); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * The next 2 defines are here bc this is the only file 2562306a36Sopenharmony_ci * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE 2662306a36Sopenharmony_ci * and not CONFIG_IPC_NS. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistruct ipc_namespace init_ipc_ns = { 2962306a36Sopenharmony_ci .ns.count = REFCOUNT_INIT(1), 3062306a36Sopenharmony_ci .user_ns = &init_user_ns, 3162306a36Sopenharmony_ci .ns.inum = PROC_IPC_INIT_INO, 3262306a36Sopenharmony_ci#ifdef CONFIG_IPC_NS 3362306a36Sopenharmony_ci .ns.ops = &ipcns_operations, 3462306a36Sopenharmony_ci#endif 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct msg_msgseg { 3862306a36Sopenharmony_ci struct msg_msgseg *next; 3962306a36Sopenharmony_ci /* the next part of the message follows immediately */ 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg)) 4362306a36Sopenharmony_ci#define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg)) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct msg_msg *alloc_msg(size_t len) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct msg_msg *msg; 4962306a36Sopenharmony_ci struct msg_msgseg **pseg; 5062306a36Sopenharmony_ci size_t alen; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci alen = min(len, DATALEN_MSG); 5362306a36Sopenharmony_ci msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT); 5462306a36Sopenharmony_ci if (msg == NULL) 5562306a36Sopenharmony_ci return NULL; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci msg->next = NULL; 5862306a36Sopenharmony_ci msg->security = NULL; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci len -= alen; 6162306a36Sopenharmony_ci pseg = &msg->next; 6262306a36Sopenharmony_ci while (len > 0) { 6362306a36Sopenharmony_ci struct msg_msgseg *seg; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci cond_resched(); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci alen = min(len, DATALEN_SEG); 6862306a36Sopenharmony_ci seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT); 6962306a36Sopenharmony_ci if (seg == NULL) 7062306a36Sopenharmony_ci goto out_err; 7162306a36Sopenharmony_ci *pseg = seg; 7262306a36Sopenharmony_ci seg->next = NULL; 7362306a36Sopenharmony_ci pseg = &seg->next; 7462306a36Sopenharmony_ci len -= alen; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return msg; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciout_err: 8062306a36Sopenharmony_ci free_msg(msg); 8162306a36Sopenharmony_ci return NULL; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct msg_msg *load_msg(const void __user *src, size_t len) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct msg_msg *msg; 8762306a36Sopenharmony_ci struct msg_msgseg *seg; 8862306a36Sopenharmony_ci int err = -EFAULT; 8962306a36Sopenharmony_ci size_t alen; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci msg = alloc_msg(len); 9262306a36Sopenharmony_ci if (msg == NULL) 9362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci alen = min(len, DATALEN_MSG); 9662306a36Sopenharmony_ci if (copy_from_user(msg + 1, src, alen)) 9762306a36Sopenharmony_ci goto out_err; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci for (seg = msg->next; seg != NULL; seg = seg->next) { 10062306a36Sopenharmony_ci len -= alen; 10162306a36Sopenharmony_ci src = (char __user *)src + alen; 10262306a36Sopenharmony_ci alen = min(len, DATALEN_SEG); 10362306a36Sopenharmony_ci if (copy_from_user(seg + 1, src, alen)) 10462306a36Sopenharmony_ci goto out_err; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci err = security_msg_msg_alloc(msg); 10862306a36Sopenharmony_ci if (err) 10962306a36Sopenharmony_ci goto out_err; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return msg; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciout_err: 11462306a36Sopenharmony_ci free_msg(msg); 11562306a36Sopenharmony_ci return ERR_PTR(err); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci#ifdef CONFIG_CHECKPOINT_RESTORE 11862306a36Sopenharmony_cistruct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct msg_msgseg *dst_pseg, *src_pseg; 12162306a36Sopenharmony_ci size_t len = src->m_ts; 12262306a36Sopenharmony_ci size_t alen; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (src->m_ts > dst->m_ts) 12562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci alen = min(len, DATALEN_MSG); 12862306a36Sopenharmony_ci memcpy(dst + 1, src + 1, alen); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci for (dst_pseg = dst->next, src_pseg = src->next; 13162306a36Sopenharmony_ci src_pseg != NULL; 13262306a36Sopenharmony_ci dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) { 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci len -= alen; 13562306a36Sopenharmony_ci alen = min(len, DATALEN_SEG); 13662306a36Sopenharmony_ci memcpy(dst_pseg + 1, src_pseg + 1, alen); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci dst->m_type = src->m_type; 14062306a36Sopenharmony_ci dst->m_ts = src->m_ts; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return dst; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci#else 14562306a36Sopenharmony_cistruct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci return ERR_PTR(-ENOSYS); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci#endif 15062306a36Sopenharmony_ciint store_msg(void __user *dest, struct msg_msg *msg, size_t len) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci size_t alen; 15362306a36Sopenharmony_ci struct msg_msgseg *seg; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci alen = min(len, DATALEN_MSG); 15662306a36Sopenharmony_ci if (copy_to_user(dest, msg + 1, alen)) 15762306a36Sopenharmony_ci return -1; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci for (seg = msg->next; seg != NULL; seg = seg->next) { 16062306a36Sopenharmony_ci len -= alen; 16162306a36Sopenharmony_ci dest = (char __user *)dest + alen; 16262306a36Sopenharmony_ci alen = min(len, DATALEN_SEG); 16362306a36Sopenharmony_ci if (copy_to_user(dest, seg + 1, alen)) 16462306a36Sopenharmony_ci return -1; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid free_msg(struct msg_msg *msg) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct msg_msgseg *seg; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci security_msg_msg_free(msg); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci seg = msg->next; 17662306a36Sopenharmony_ci kfree(msg); 17762306a36Sopenharmony_ci while (seg != NULL) { 17862306a36Sopenharmony_ci struct msg_msgseg *tmp = seg->next; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci cond_resched(); 18162306a36Sopenharmony_ci kfree(seg); 18262306a36Sopenharmony_ci seg = tmp; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci} 185