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
22 DEFINE_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 */
29 struct 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
39 struct 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
47 static kmem_buckets *msg_buckets __ro_after_init;
48
init_msg_buckets(void)49 static int __init init_msg_buckets(void)
50 {
51 msg_buckets = kmem_buckets_create("msg_msg", SLAB_ACCOUNT,
52 sizeof(struct msg_msg),
53 DATALEN_MSG, NULL);
54
55 return 0;
56 }
57 subsys_initcall(init_msg_buckets);
58
alloc_msg(size_t len)59 static 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
92 out_err:
93 free_msg(msg);
94 return NULL;
95 }
96
load_msg(const void __user * src,size_t len)97 struct 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(-ENOMEM);
107
108 alen = min(len, DATALEN_MSG);
109 if (copy_from_user(msg + 1, src, 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(seg + 1, src, 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
126 out_err:
127 free_msg(msg);
128 return ERR_PTR(err);
129 }
130 #ifdef CONFIG_CHECKPOINT_RESTORE
copy_msg(struct msg_msg * src,struct msg_msg * dst)131 struct 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
copy_msg(struct msg_msg * src,struct msg_msg * dst)158 struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
159 {
160 return ERR_PTR(-ENOSYS);
161 }
162 #endif
store_msg(void __user * dest,struct msg_msg * msg,size_t len)163 int 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(dest, msg + 1, 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(dest, seg + 1, alen))
177 return -1;
178 }
179 return 0;
180 }
181
free_msg(struct msg_msg * msg)182 void 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(msg);
190 while (seg != NULL) {
191 struct msg_msgseg *tmp = seg->next;
192
193 cond_resched();
194 kfree(seg);
195 seg = tmp;
196 }
197 }
198