xref: /linux/ipc/msgutil.c (revision 4f77c3462308c62ffe7129cc18b9ac937f44b5a5)
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 
19 #include "util.h"
20 
21 DEFINE_SPINLOCK(mq_lock);
22 
23 /*
24  * The next 2 defines are here bc this is the only file
25  * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
26  * and not CONFIG_IPC_NS.
27  */
28 struct 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_NS
33 	.ns.ops = &ipcns_operations,
34 #endif
35 };
36 
37 struct 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 
45 static kmem_buckets *msg_buckets __ro_after_init;
46 
47 static 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 }
55 subsys_initcall(init_msg_buckets);
56 
57 static 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 
90 out_err:
91 	free_msg(msg);
92 	return NULL;
93 }
94 
95 struct 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 
124 out_err:
125 	free_msg(msg);
126 	return ERR_PTR(err);
127 }
128 #ifdef CONFIG_CHECKPOINT_RESTORE
129 struct 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 #else
156 struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
157 {
158 	return ERR_PTR(-ENOSYS);
159 }
160 #endif
161 int 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 
180 void 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