196 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * linux/ipc/msgutil.c4 * Copyright (C) 1999, 2004 Manfred Spraul5 */6 7#include <linux/spinlock.h>8#include <linux/init.h>9#include <linux/security.h>10#include <linux/slab.h>11#include <linux/ipc.h>12#include <linux/msg.h>13#include <linux/ipc_namespace.h>14#include <linux/utsname.h>15#include <linux/proc_ns.h>16#include <linux/uaccess.h>17#include <linux/sched.h>18 19#include "util.h"20 21DEFINE_SPINLOCK(mq_lock);22 23/*24 * The next 2 defines are here bc this is the only file25 * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE26 * and not CONFIG_IPC_NS.27 */28struct ipc_namespace init_ipc_ns = {29 .ns.count = REFCOUNT_INIT(1),30 .user_ns = &init_user_ns,31 .ns.inum = PROC_IPC_INIT_INO,32#ifdef CONFIG_IPC_NS33 .ns.ops = &ipcns_operations,34#endif35};36 37struct msg_msgseg {38 struct msg_msgseg *next;39 /* the next part of the message follows immediately */40};41 42#define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg))43#define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))44 45static kmem_buckets *msg_buckets __ro_after_init;46 47static int __init init_msg_buckets(void)48{49 msg_buckets = kmem_buckets_create("msg_msg", SLAB_ACCOUNT,50 sizeof(struct msg_msg),51 DATALEN_MSG, NULL);52 53 return 0;54}55subsys_initcall(init_msg_buckets);56 57static struct msg_msg *alloc_msg(size_t len)58{59 struct msg_msg *msg;60 struct msg_msgseg **pseg;61 size_t alen;62 63 alen = min(len, DATALEN_MSG);64 msg = kmem_buckets_alloc(msg_buckets, sizeof(*msg) + alen, GFP_KERNEL);65 if (msg == NULL)66 return NULL;67 68 msg->next = NULL;69 msg->security = NULL;70 71 len -= alen;72 pseg = &msg->next;73 while (len > 0) {74 struct msg_msgseg *seg;75 76 cond_resched();77 78 alen = min(len, DATALEN_SEG);79 seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);80 if (seg == NULL)81 goto out_err;82 *pseg = seg;83 seg->next = NULL;84 pseg = &seg->next;85 len -= alen;86 }87 88 return msg;89 90out_err:91 free_msg(msg);92 return NULL;93}94 95struct msg_msg *load_msg(const void __user *src, size_t len)96{97 struct msg_msg *msg;98 struct msg_msgseg *seg;99 int err = -EFAULT;100 size_t alen;101 102 msg = alloc_msg(len);103 if (msg == NULL)104 return ERR_PTR(-ENOMEM);105 106 alen = min(len, DATALEN_MSG);107 if (copy_from_user(msg + 1, src, alen))108 goto out_err;109 110 for (seg = msg->next; seg != NULL; seg = seg->next) {111 len -= alen;112 src = (char __user *)src + alen;113 alen = min(len, DATALEN_SEG);114 if (copy_from_user(seg + 1, src, alen))115 goto out_err;116 }117 118 err = security_msg_msg_alloc(msg);119 if (err)120 goto out_err;121 122 return msg;123 124out_err:125 free_msg(msg);126 return ERR_PTR(err);127}128#ifdef CONFIG_CHECKPOINT_RESTORE129struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)130{131 struct msg_msgseg *dst_pseg, *src_pseg;132 size_t len = src->m_ts;133 size_t alen;134 135 if (src->m_ts > dst->m_ts)136 return ERR_PTR(-EINVAL);137 138 alen = min(len, DATALEN_MSG);139 memcpy(dst + 1, src + 1, alen);140 141 for (dst_pseg = dst->next, src_pseg = src->next;142 src_pseg != NULL;143 dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {144 145 len -= alen;146 alen = min(len, DATALEN_SEG);147 memcpy(dst_pseg + 1, src_pseg + 1, alen);148 }149 150 dst->m_type = src->m_type;151 dst->m_ts = src->m_ts;152 153 return dst;154}155#else156struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)157{158 return ERR_PTR(-ENOSYS);159}160#endif161int store_msg(void __user *dest, struct msg_msg *msg, size_t len)162{163 size_t alen;164 struct msg_msgseg *seg;165 166 alen = min(len, DATALEN_MSG);167 if (copy_to_user(dest, msg + 1, alen))168 return -1;169 170 for (seg = msg->next; seg != NULL; seg = seg->next) {171 len -= alen;172 dest = (char __user *)dest + alen;173 alen = min(len, DATALEN_SEG);174 if (copy_to_user(dest, seg + 1, alen))175 return -1;176 }177 return 0;178}179 180void free_msg(struct msg_msg *msg)181{182 struct msg_msgseg *seg;183 184 security_msg_msg_free(msg);185 186 seg = msg->next;187 kfree(msg);188 while (seg != NULL) {189 struct msg_msgseg *tmp = seg->next;190 191 cond_resched();192 kfree(seg);193 seg = tmp;194 }195}196