xref: /linux/ipc/msgutil.c (revision 18b19abc3709b109676ffd1f48dcd332c2e477d4)
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