blob: e65593742e2be5965f45929d485b54e8707f6d0d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Uwe Zeisbergerf30c2262006-10-03 23:01:26 +02002 * linux/ipc/msgutil.c
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Copyright (C) 1999, 2004 Manfred Spraul
4 *
5 * This file is released under GNU General Public Licence version 2 or
6 * (at your option) any later version.
7 *
8 * See the file COPYING for more details.
9 */
10
11#include <linux/spinlock.h>
12#include <linux/init.h>
13#include <linux/security.h>
14#include <linux/slab.h>
15#include <linux/ipc.h>
Al Viro40401532012-02-13 03:58:52 +000016#include <linux/msg.h>
Serge E. Hallyn614b84c2009-04-06 19:01:08 -070017#include <linux/ipc_namespace.h>
Al Viro40401532012-02-13 03:58:52 +000018#include <linux/utsname.h>
David Howells0bb80f22013-04-12 01:50:06 +010019#include <linux/proc_ns.h>
HoSung Jung1e3c9412013-04-30 19:15:09 -070020#include <linux/uaccess.h>
Li Rongqingd6a29462019-05-14 15:46:20 -070021#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
23#include "util.h"
24
Serge E. Hallyn7eafd7c2009-04-06 19:01:10 -070025DEFINE_SPINLOCK(mq_lock);
26
Serge E. Hallyn614b84c2009-04-06 19:01:08 -070027/*
28 * The next 2 defines are here bc this is the only file
29 * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
30 * and not CONFIG_IPC_NS.
31 */
32struct ipc_namespace init_ipc_ns = {
Elena Reshetovaa2e06022017-09-08 16:17:38 -070033 .count = REFCOUNT_INIT(1),
Serge E. Hallynb5154982011-03-23 16:43:23 -070034 .user_ns = &init_user_ns,
Al Viro435d5f42014-10-31 22:56:04 -040035 .ns.inum = PROC_IPC_INIT_INO,
Al Viro33c42942014-11-01 02:32:53 -040036#ifdef CONFIG_IPC_NS
37 .ns.ops = &ipcns_operations,
38#endif
Serge E. Hallyn614b84c2009-04-06 19:01:08 -070039};
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041struct msg_msgseg {
HoSung Jung1e3c9412013-04-30 19:15:09 -070042 struct msg_msgseg *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 /* the next part of the message follows immediately */
44};
45
Mathias Krause4e9b45a2013-11-12 15:11:47 -080046#define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg))
47#define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Peter Hurleybe5f4b32013-04-30 19:14:31 -070049
Mathias Krause4e9b45a2013-11-12 15:11:47 -080050static struct msg_msg *alloc_msg(size_t len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070051{
52 struct msg_msg *msg;
53 struct msg_msgseg **pseg;
Mathias Krause4e9b45a2013-11-12 15:11:47 -080054 size_t alen;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
Peter Hurley3d8fa452013-04-30 19:14:25 -070056 alen = min(len, DATALEN_MSG);
Aristeu Rozanski8c8d4d42016-10-27 17:46:35 -070057 msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 if (msg == NULL)
Peter Hurleybe5f4b32013-04-30 19:14:31 -070059 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61 msg->next = NULL;
62 msg->security = NULL;
63
Peter Hurleybe5f4b32013-04-30 19:14:31 -070064 len -= alen;
65 pseg = &msg->next;
66 while (len > 0) {
67 struct msg_msgseg *seg;
Li Rongqingd6a29462019-05-14 15:46:20 -070068
69 cond_resched();
70
Peter Hurleybe5f4b32013-04-30 19:14:31 -070071 alen = min(len, DATALEN_SEG);
Aristeu Rozanski8c8d4d42016-10-27 17:46:35 -070072 seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);
Peter Hurleybe5f4b32013-04-30 19:14:31 -070073 if (seg == NULL)
74 goto out_err;
75 *pseg = seg;
76 seg->next = NULL;
77 pseg = &seg->next;
78 len -= alen;
79 }
80
81 return msg;
82
83out_err:
84 free_msg(msg);
85 return NULL;
86}
87
Mathias Krause4e9b45a2013-11-12 15:11:47 -080088struct msg_msg *load_msg(const void __user *src, size_t len)
Peter Hurleybe5f4b32013-04-30 19:14:31 -070089{
90 struct msg_msg *msg;
91 struct msg_msgseg *seg;
Peter Hurley2b3097a2013-04-30 19:14:42 -070092 int err = -EFAULT;
Mathias Krause4e9b45a2013-11-12 15:11:47 -080093 size_t alen;
Peter Hurleybe5f4b32013-04-30 19:14:31 -070094
95 msg = alloc_msg(len);
96 if (msg == NULL)
97 return ERR_PTR(-ENOMEM);
98
99 alen = min(len, DATALEN_MSG);
Peter Hurley2b3097a2013-04-30 19:14:42 -0700100 if (copy_from_user(msg + 1, src, alen))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
Peter Hurleyda085d42013-04-30 19:14:37 -0700103 for (seg = msg->next; seg != NULL; seg = seg->next) {
104 len -= alen;
105 src = (char __user *)src + alen;
Peter Hurley3d8fa452013-04-30 19:14:25 -0700106 alen = min(len, DATALEN_SEG);
Peter Hurley2b3097a2013-04-30 19:14:42 -0700107 if (copy_from_user(seg + 1, src, alen))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 }
110
111 err = security_msg_msg_alloc(msg);
112 if (err)
113 goto out_err;
114
115 return msg;
116
117out_err:
118 free_msg(msg);
119 return ERR_PTR(err);
120}
Stanislav Kinsbursky4a674f32013-01-04 15:34:55 -0800121#ifdef CONFIG_CHECKPOINT_RESTORE
122struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
123{
124 struct msg_msgseg *dst_pseg, *src_pseg;
Mathias Krause4e9b45a2013-11-12 15:11:47 -0800125 size_t len = src->m_ts;
126 size_t alen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
Stanislav Kinsbursky4a674f32013-01-04 15:34:55 -0800128 if (src->m_ts > dst->m_ts)
129 return ERR_PTR(-EINVAL);
130
Peter Hurley3d8fa452013-04-30 19:14:25 -0700131 alen = min(len, DATALEN_MSG);
Stanislav Kinsbursky4a674f32013-01-04 15:34:55 -0800132 memcpy(dst + 1, src + 1, alen);
133
Peter Hurleyda085d42013-04-30 19:14:37 -0700134 for (dst_pseg = dst->next, src_pseg = src->next;
135 src_pseg != NULL;
136 dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
137
138 len -= alen;
Peter Hurley3d8fa452013-04-30 19:14:25 -0700139 alen = min(len, DATALEN_SEG);
Stanislav Kinsbursky4a674f32013-01-04 15:34:55 -0800140 memcpy(dst_pseg + 1, src_pseg + 1, alen);
Stanislav Kinsbursky4a674f32013-01-04 15:34:55 -0800141 }
142
143 dst->m_type = src->m_type;
144 dst->m_ts = src->m_ts;
145
146 return dst;
147}
Stanislav Kinsbursky51eeaca2013-01-04 15:35:01 -0800148#else
149struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
150{
151 return ERR_PTR(-ENOSYS);
152}
Stanislav Kinsbursky4a674f32013-01-04 15:34:55 -0800153#endif
Mathias Krause4e9b45a2013-11-12 15:11:47 -0800154int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155{
Mathias Krause4e9b45a2013-11-12 15:11:47 -0800156 size_t alen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 struct msg_msgseg *seg;
158
Peter Hurley3d8fa452013-04-30 19:14:25 -0700159 alen = min(len, DATALEN_MSG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 if (copy_to_user(dest, msg + 1, alen))
161 return -1;
162
Peter Hurleyda085d42013-04-30 19:14:37 -0700163 for (seg = msg->next; seg != NULL; seg = seg->next) {
164 len -= alen;
165 dest = (char __user *)dest + alen;
Peter Hurley3d8fa452013-04-30 19:14:25 -0700166 alen = min(len, DATALEN_SEG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 if (copy_to_user(dest, seg + 1, alen))
168 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 }
170 return 0;
171}
172
173void free_msg(struct msg_msg *msg)
174{
175 struct msg_msgseg *seg;
176
177 security_msg_msg_free(msg);
178
179 seg = msg->next;
180 kfree(msg);
181 while (seg != NULL) {
182 struct msg_msgseg *tmp = seg->next;
Li Rongqingd6a29462019-05-14 15:46:20 -0700183
184 cond_resched();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 kfree(seg);
186 seg = tmp;
187 }
188}