xref: /openbmc/linux/ipc/msgutil.c (revision c40d04df)
1 /*
2  * linux/ipc/msgutil.c
3  * 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>
16 #include <linux/msg.h>
17 #include <linux/ipc_namespace.h>
18 #include <linux/utsname.h>
19 #include <asm/uaccess.h>
20 
21 #include "util.h"
22 
23 DEFINE_SPINLOCK(mq_lock);
24 
25 /*
26  * The next 2 defines are here bc this is the only file
27  * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
28  * and not CONFIG_IPC_NS.
29  */
30 struct ipc_namespace init_ipc_ns = {
31 	.count		= ATOMIC_INIT(1),
32 	.user_ns = &init_user_ns,
33 };
34 
35 atomic_t nr_ipc_ns = ATOMIC_INIT(1);
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	(PAGE_SIZE-sizeof(struct msg_msg))
43 #define DATALEN_SEG	(PAGE_SIZE-sizeof(struct msg_msgseg))
44 
45 struct msg_msg *load_msg(const void __user *src, int len)
46 {
47 	struct msg_msg *msg;
48 	struct msg_msgseg **pseg;
49 	int err;
50 	int alen;
51 
52 	alen = len;
53 	if (alen > DATALEN_MSG)
54 		alen = DATALEN_MSG;
55 
56 	msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
57 	if (msg == NULL)
58 		return ERR_PTR(-ENOMEM);
59 
60 	msg->next = NULL;
61 	msg->security = NULL;
62 
63 	if (copy_from_user(msg + 1, src, alen)) {
64 		err = -EFAULT;
65 		goto out_err;
66 	}
67 
68 	len -= alen;
69 	src = ((char __user *)src) + alen;
70 	pseg = &msg->next;
71 	while (len > 0) {
72 		struct msg_msgseg *seg;
73 		alen = len;
74 		if (alen > DATALEN_SEG)
75 			alen = DATALEN_SEG;
76 		seg = kmalloc(sizeof(*seg) + alen,
77 						 GFP_KERNEL);
78 		if (seg == NULL) {
79 			err = -ENOMEM;
80 			goto out_err;
81 		}
82 		*pseg = seg;
83 		seg->next = NULL;
84 		if (copy_from_user(seg + 1, src, alen)) {
85 			err = -EFAULT;
86 			goto out_err;
87 		}
88 		pseg = &seg->next;
89 		len -= alen;
90 		src = ((char __user *)src) + alen;
91 	}
92 
93 	err = security_msg_msg_alloc(msg);
94 	if (err)
95 		goto out_err;
96 
97 	return msg;
98 
99 out_err:
100 	free_msg(msg);
101 	return ERR_PTR(err);
102 }
103 
104 int store_msg(void __user *dest, struct msg_msg *msg, int len)
105 {
106 	int alen;
107 	struct msg_msgseg *seg;
108 
109 	alen = len;
110 	if (alen > DATALEN_MSG)
111 		alen = DATALEN_MSG;
112 	if (copy_to_user(dest, msg + 1, alen))
113 		return -1;
114 
115 	len -= alen;
116 	dest = ((char __user *)dest) + alen;
117 	seg = msg->next;
118 	while (len > 0) {
119 		alen = len;
120 		if (alen > DATALEN_SEG)
121 			alen = DATALEN_SEG;
122 		if (copy_to_user(dest, seg + 1, alen))
123 			return -1;
124 		len -= alen;
125 		dest = ((char __user *)dest) + alen;
126 		seg = seg->next;
127 	}
128 	return 0;
129 }
130 
131 void free_msg(struct msg_msg *msg)
132 {
133 	struct msg_msgseg *seg;
134 
135 	security_msg_msg_free(msg);
136 
137 	seg = msg->next;
138 	kfree(msg);
139 	while (seg != NULL) {
140 		struct msg_msgseg *tmp = seg->next;
141 		kfree(seg);
142 		seg = tmp;
143 	}
144 }
145