xref: /linux/ipc/msgutil.c (revision 7ebdfaa52d15b947503f76474477f92854796d96)
11da177e4SLinus Torvalds /*
2f30c2269SUwe Zeisberger  * linux/ipc/msgutil.c
31da177e4SLinus Torvalds  * Copyright (C) 1999, 2004 Manfred Spraul
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * This file is released under GNU General Public Licence version 2 or
61da177e4SLinus Torvalds  * (at your option) any later version.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * See the file COPYING for more details.
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/spinlock.h>
121da177e4SLinus Torvalds #include <linux/init.h>
131da177e4SLinus Torvalds #include <linux/security.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/ipc.h>
1640401530SAl Viro #include <linux/msg.h>
17614b84cfSSerge E. Hallyn #include <linux/ipc_namespace.h>
1840401530SAl Viro #include <linux/utsname.h>
190bb80f24SDavid Howells #include <linux/proc_ns.h>
201e3c941cSHoSung Jung #include <linux/uaccess.h>
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #include "util.h"
231da177e4SLinus Torvalds 
247eafd7c7SSerge E. Hallyn DEFINE_SPINLOCK(mq_lock);
257eafd7c7SSerge E. Hallyn 
26614b84cfSSerge E. Hallyn /*
27614b84cfSSerge E. Hallyn  * The next 2 defines are here bc this is the only file
28614b84cfSSerge E. Hallyn  * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
29614b84cfSSerge E. Hallyn  * and not CONFIG_IPC_NS.
30614b84cfSSerge E. Hallyn  */
31614b84cfSSerge E. Hallyn struct ipc_namespace init_ipc_ns = {
327eafd7c7SSerge E. Hallyn 	.count		= ATOMIC_INIT(1),
33b515498fSSerge E. Hallyn 	.user_ns = &init_user_ns,
34435d5f4bSAl Viro 	.ns.inum = PROC_IPC_INIT_INO,
35*33c42940SAl Viro #ifdef CONFIG_IPC_NS
36*33c42940SAl Viro 	.ns.ops = &ipcns_operations,
37*33c42940SAl Viro #endif
38614b84cfSSerge E. Hallyn };
39614b84cfSSerge E. Hallyn 
40614b84cfSSerge E. Hallyn atomic_t nr_ipc_ns = ATOMIC_INIT(1);
41614b84cfSSerge E. Hallyn 
421da177e4SLinus Torvalds struct msg_msgseg {
431da177e4SLinus Torvalds 	struct msg_msgseg *next;
441da177e4SLinus Torvalds 	/* the next part of the message follows immediately */
451da177e4SLinus Torvalds };
461da177e4SLinus Torvalds 
474e9b45a1SMathias Krause #define DATALEN_MSG	((size_t)PAGE_SIZE-sizeof(struct msg_msg))
484e9b45a1SMathias Krause #define DATALEN_SEG	((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
491da177e4SLinus Torvalds 
50be5f4b33SPeter Hurley 
514e9b45a1SMathias Krause static struct msg_msg *alloc_msg(size_t len)
521da177e4SLinus Torvalds {
531da177e4SLinus Torvalds 	struct msg_msg *msg;
541da177e4SLinus Torvalds 	struct msg_msgseg **pseg;
554e9b45a1SMathias Krause 	size_t alen;
561da177e4SLinus Torvalds 
573d8fa456SPeter Hurley 	alen = min(len, DATALEN_MSG);
585cbded58SRobert P. J. Day 	msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
591da177e4SLinus Torvalds 	if (msg == NULL)
60be5f4b33SPeter Hurley 		return NULL;
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	msg->next = NULL;
631da177e4SLinus Torvalds 	msg->security = NULL;
641da177e4SLinus Torvalds 
65be5f4b33SPeter Hurley 	len -= alen;
66be5f4b33SPeter Hurley 	pseg = &msg->next;
67be5f4b33SPeter Hurley 	while (len > 0) {
68be5f4b33SPeter Hurley 		struct msg_msgseg *seg;
69be5f4b33SPeter Hurley 		alen = min(len, DATALEN_SEG);
70be5f4b33SPeter Hurley 		seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL);
71be5f4b33SPeter Hurley 		if (seg == NULL)
72be5f4b33SPeter Hurley 			goto out_err;
73be5f4b33SPeter Hurley 		*pseg = seg;
74be5f4b33SPeter Hurley 		seg->next = NULL;
75be5f4b33SPeter Hurley 		pseg = &seg->next;
76be5f4b33SPeter Hurley 		len -= alen;
77be5f4b33SPeter Hurley 	}
78be5f4b33SPeter Hurley 
79be5f4b33SPeter Hurley 	return msg;
80be5f4b33SPeter Hurley 
81be5f4b33SPeter Hurley out_err:
82be5f4b33SPeter Hurley 	free_msg(msg);
83be5f4b33SPeter Hurley 	return NULL;
84be5f4b33SPeter Hurley }
85be5f4b33SPeter Hurley 
864e9b45a1SMathias Krause struct msg_msg *load_msg(const void __user *src, size_t len)
87be5f4b33SPeter Hurley {
88be5f4b33SPeter Hurley 	struct msg_msg *msg;
89be5f4b33SPeter Hurley 	struct msg_msgseg *seg;
902b3097a2SPeter Hurley 	int err = -EFAULT;
914e9b45a1SMathias Krause 	size_t alen;
92be5f4b33SPeter Hurley 
93be5f4b33SPeter Hurley 	msg = alloc_msg(len);
94be5f4b33SPeter Hurley 	if (msg == NULL)
95be5f4b33SPeter Hurley 		return ERR_PTR(-ENOMEM);
96be5f4b33SPeter Hurley 
97be5f4b33SPeter Hurley 	alen = min(len, DATALEN_MSG);
982b3097a2SPeter Hurley 	if (copy_from_user(msg + 1, src, alen))
991da177e4SLinus Torvalds 		goto out_err;
1001da177e4SLinus Torvalds 
101da085d45SPeter Hurley 	for (seg = msg->next; seg != NULL; seg = seg->next) {
1021da177e4SLinus Torvalds 		len -= alen;
103da085d45SPeter Hurley 		src = (char __user *)src + alen;
1043d8fa456SPeter Hurley 		alen = min(len, DATALEN_SEG);
1052b3097a2SPeter Hurley 		if (copy_from_user(seg + 1, src, alen))
1061da177e4SLinus Torvalds 			goto out_err;
1071da177e4SLinus Torvalds 	}
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	err = security_msg_msg_alloc(msg);
1101da177e4SLinus Torvalds 	if (err)
1111da177e4SLinus Torvalds 		goto out_err;
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 	return msg;
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds out_err:
1161da177e4SLinus Torvalds 	free_msg(msg);
1171da177e4SLinus Torvalds 	return ERR_PTR(err);
1181da177e4SLinus Torvalds }
1194a674f34SStanislav Kinsbursky #ifdef CONFIG_CHECKPOINT_RESTORE
1204a674f34SStanislav Kinsbursky struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
1214a674f34SStanislav Kinsbursky {
1224a674f34SStanislav Kinsbursky 	struct msg_msgseg *dst_pseg, *src_pseg;
1234e9b45a1SMathias Krause 	size_t len = src->m_ts;
1244e9b45a1SMathias Krause 	size_t alen;
1251da177e4SLinus Torvalds 
1264a674f34SStanislav Kinsbursky 	BUG_ON(dst == NULL);
1274a674f34SStanislav Kinsbursky 	if (src->m_ts > dst->m_ts)
1284a674f34SStanislav Kinsbursky 		return ERR_PTR(-EINVAL);
1294a674f34SStanislav Kinsbursky 
1303d8fa456SPeter Hurley 	alen = min(len, DATALEN_MSG);
1314a674f34SStanislav Kinsbursky 	memcpy(dst + 1, src + 1, alen);
1324a674f34SStanislav Kinsbursky 
133da085d45SPeter Hurley 	for (dst_pseg = dst->next, src_pseg = src->next;
134da085d45SPeter Hurley 	     src_pseg != NULL;
135da085d45SPeter Hurley 	     dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
136da085d45SPeter Hurley 
1374a674f34SStanislav Kinsbursky 		len -= alen;
1383d8fa456SPeter Hurley 		alen = min(len, DATALEN_SEG);
1394a674f34SStanislav Kinsbursky 		memcpy(dst_pseg + 1, src_pseg + 1, alen);
1404a674f34SStanislav Kinsbursky 	}
1414a674f34SStanislav Kinsbursky 
1424a674f34SStanislav Kinsbursky 	dst->m_type = src->m_type;
1434a674f34SStanislav Kinsbursky 	dst->m_ts = src->m_ts;
1444a674f34SStanislav Kinsbursky 
1454a674f34SStanislav Kinsbursky 	return dst;
1464a674f34SStanislav Kinsbursky }
14751eeacaaSStanislav Kinsbursky #else
14851eeacaaSStanislav Kinsbursky struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
14951eeacaaSStanislav Kinsbursky {
15051eeacaaSStanislav Kinsbursky 	return ERR_PTR(-ENOSYS);
15151eeacaaSStanislav Kinsbursky }
1524a674f34SStanislav Kinsbursky #endif
1534e9b45a1SMathias Krause int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
1541da177e4SLinus Torvalds {
1554e9b45a1SMathias Krause 	size_t alen;
1561da177e4SLinus Torvalds 	struct msg_msgseg *seg;
1571da177e4SLinus Torvalds 
1583d8fa456SPeter Hurley 	alen = min(len, DATALEN_MSG);
1591da177e4SLinus Torvalds 	if (copy_to_user(dest, msg + 1, alen))
1601da177e4SLinus Torvalds 		return -1;
1611da177e4SLinus Torvalds 
162da085d45SPeter Hurley 	for (seg = msg->next; seg != NULL; seg = seg->next) {
1631da177e4SLinus Torvalds 		len -= alen;
164da085d45SPeter Hurley 		dest = (char __user *)dest + alen;
1653d8fa456SPeter Hurley 		alen = min(len, DATALEN_SEG);
1661da177e4SLinus Torvalds 		if (copy_to_user(dest, seg + 1, alen))
1671da177e4SLinus Torvalds 			return -1;
1681da177e4SLinus Torvalds 	}
1691da177e4SLinus Torvalds 	return 0;
1701da177e4SLinus Torvalds }
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds void free_msg(struct msg_msg *msg)
1731da177e4SLinus Torvalds {
1741da177e4SLinus Torvalds 	struct msg_msgseg *seg;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	security_msg_msg_free(msg);
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	seg = msg->next;
1791da177e4SLinus Torvalds 	kfree(msg);
1801da177e4SLinus Torvalds 	while (seg != NULL) {
1811da177e4SLinus Torvalds 		struct msg_msgseg *tmp = seg->next;
1821da177e4SLinus Torvalds 		kfree(seg);
1831da177e4SLinus Torvalds 		seg = tmp;
1841da177e4SLinus Torvalds 	}
1851da177e4SLinus Torvalds }
186