xref: /linux/ipc/msgutil.c (revision c57fbbe42e907fa26c5ffada52d913d4ca5fa66e)
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_COMMON_INIT(init_ipc_ns),
31 	.user_ns = &init_user_ns,
32 };
33 
34 struct msg_msgseg {
35 	struct msg_msgseg *next;
36 	/* the next part of the message follows immediately */
37 };
38 
39 #define DATALEN_MSG	((size_t)PAGE_SIZE-sizeof(struct msg_msg))
40 #define DATALEN_SEG	((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
41 
42 static kmem_buckets *msg_buckets __ro_after_init;
43 
44 static int __init init_msg_buckets(void)
45 {
46 	msg_buckets = kmem_buckets_create("msg_msg", SLAB_ACCOUNT,
47 					  sizeof(struct msg_msg),
48 					  DATALEN_MSG, NULL);
49 
50 	return 0;
51 }
52 subsys_initcall(init_msg_buckets);
53 
54 static struct msg_msg *alloc_msg(size_t len)
55 {
56 	struct msg_msg *msg;
57 	struct msg_msgseg **pseg;
58 	size_t alen;
59 
60 	alen = min(len, DATALEN_MSG);
61 	msg = kmem_buckets_alloc(msg_buckets, sizeof(*msg) + alen,
62 				 GFP_KERNEL_ACCOUNT);
63 	if (msg == NULL)
64 		return NULL;
65 
66 	msg->next = NULL;
67 	msg->security = NULL;
68 
69 	len -= alen;
70 	pseg = &msg->next;
71 	while (len > 0) {
72 		struct msg_msgseg *seg;
73 
74 		cond_resched();
75 
76 		alen = min(len, DATALEN_SEG);
77 		seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);
78 		if (seg == NULL)
79 			goto out_err;
80 		*pseg = seg;
81 		seg->next = NULL;
82 		pseg = &seg->next;
83 		len -= alen;
84 	}
85 
86 	return msg;
87 
88 out_err:
89 	free_msg(msg);
90 	return NULL;
91 }
92 
93 struct msg_msg *load_msg(const void __user *src, size_t len)
94 {
95 	struct msg_msg *msg;
96 	struct msg_msgseg *seg;
97 	int err = -EFAULT;
98 	size_t alen;
99 
100 	msg = alloc_msg(len);
101 	if (msg == NULL)
102 		return ERR_PTR(-ENOMEM);
103 
104 	alen = min(len, DATALEN_MSG);
105 	if (copy_from_user(msg + 1, src, alen))
106 		goto out_err;
107 
108 	for (seg = msg->next; seg != NULL; seg = seg->next) {
109 		len -= alen;
110 		src = (char __user *)src + alen;
111 		alen = min(len, DATALEN_SEG);
112 		if (copy_from_user(seg + 1, src, alen))
113 			goto out_err;
114 	}
115 
116 	err = security_msg_msg_alloc(msg);
117 	if (err)
118 		goto out_err;
119 
120 	return msg;
121 
122 out_err:
123 	free_msg(msg);
124 	return ERR_PTR(err);
125 }
126 #ifdef CONFIG_CHECKPOINT_RESTORE
127 struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
128 {
129 	struct msg_msgseg *dst_pseg, *src_pseg;
130 	size_t len = src->m_ts;
131 	size_t alen;
132 
133 	if (src->m_ts > dst->m_ts)
134 		return ERR_PTR(-EINVAL);
135 
136 	alen = min(len, DATALEN_MSG);
137 	memcpy(dst + 1, src + 1, alen);
138 
139 	for (dst_pseg = dst->next, src_pseg = src->next;
140 	     src_pseg != NULL;
141 	     dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
142 
143 		len -= alen;
144 		alen = min(len, DATALEN_SEG);
145 		memcpy(dst_pseg + 1, src_pseg + 1, alen);
146 	}
147 
148 	dst->m_type = src->m_type;
149 	dst->m_ts = src->m_ts;
150 
151 	return dst;
152 }
153 #else
154 struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
155 {
156 	return ERR_PTR(-ENOSYS);
157 }
158 #endif
159 int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
160 {
161 	size_t alen;
162 	struct msg_msgseg *seg;
163 
164 	alen = min(len, DATALEN_MSG);
165 	if (copy_to_user(dest, msg + 1, alen))
166 		return -1;
167 
168 	for (seg = msg->next; seg != NULL; seg = seg->next) {
169 		len -= alen;
170 		dest = (char __user *)dest + alen;
171 		alen = min(len, DATALEN_SEG);
172 		if (copy_to_user(dest, seg + 1, alen))
173 			return -1;
174 	}
175 	return 0;
176 }
177 
178 void free_msg(struct msg_msg *msg)
179 {
180 	struct msg_msgseg *seg;
181 
182 	security_msg_msg_free(msg);
183 
184 	seg = msg->next;
185 	kfree(msg);
186 	while (seg != NULL) {
187 		struct msg_msgseg *tmp = seg->next;
188 
189 		cond_resched();
190 		kfree(seg);
191 		seg = tmp;
192 	}
193 }
194