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