]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.4.51/net-block-msg_cmsg_compat-in-send-m-msg-and-recv-m-msg.patch
4.14-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.4.51 / net-block-msg_cmsg_compat-in-send-m-msg-and-recv-m-msg.patch
1 From d82ab26fe98468628ffccc82bdb3d970b28ff200 Mon Sep 17 00:00:00 2001
2 From: Andy Lutomirski <luto@amacapital.net>
3 Date: Wed, 22 May 2013 14:07:44 -0700
4 Subject: net: Block MSG_CMSG_COMPAT in send(m)msg and recv(m)msg
5
6 From: Andy Lutomirski <luto@amacapital.net>
7
8 [ Upstream commits 1be374a0518a288147c6a7398792583200a67261 and
9 a7526eb5d06b0084ef12d7b168d008fcf516caab ]
10
11 MSG_CMSG_COMPAT is (AFAIK) not intended to be part of the API --
12 it's a hack that steals a bit to indicate to other networking code
13 that a compat entry was used. So don't allow it from a non-compat
14 syscall.
15
16 This prevents an oops when running this code:
17
18 int main()
19 {
20 int s;
21 struct sockaddr_in addr;
22 struct msghdr *hdr;
23
24 char *highpage = mmap((void*)(TASK_SIZE_MAX - 4096), 4096,
25 PROT_READ | PROT_WRITE,
26 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
27 if (highpage == MAP_FAILED)
28 err(1, "mmap");
29
30 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
31 if (s == -1)
32 err(1, "socket");
33
34 addr.sin_family = AF_INET;
35 addr.sin_port = htons(1);
36 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
37 if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) != 0)
38 err(1, "connect");
39
40 void *evil = highpage + 4096 - COMPAT_MSGHDR_SIZE;
41 printf("Evil address is %p\n", evil);
42
43 if (syscall(__NR_sendmmsg, s, evil, 1, MSG_CMSG_COMPAT) < 0)
44 err(1, "sendmmsg");
45
46 return 0;
47 }
48
49 Signed-off-by: Andy Lutomirski <luto@amacapital.net>
50 Cc: David S. Miller <davem@davemloft.net>
51 Signed-off-by: David S. Miller <davem@davemloft.net>
52 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
53 ---
54 include/linux/socket.h | 3 ++
55 net/compat.c | 13 ++++++++-
56 net/socket.c | 67 ++++++++++++++++++++++++++++++++-----------------
57 3 files changed, 59 insertions(+), 24 deletions(-)
58
59 --- a/include/linux/socket.h
60 +++ b/include/linux/socket.h
61 @@ -336,6 +336,9 @@ extern int put_cmsg(struct msghdr*, int
62
63 struct timespec;
64
65 +/* The __sys_...msg variants allow MSG_CMSG_COMPAT */
66 +extern long __sys_recvmsg(int fd, struct msghdr __user *msg, unsigned flags);
67 +extern long __sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags);
68 extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
69 unsigned int flags, struct timespec *timeout);
70 extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg,
71 --- a/net/compat.c
72 +++ b/net/compat.c
73 @@ -743,19 +743,25 @@ static unsigned char nas[21] = {
74
75 asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags)
76 {
77 - return sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
78 + if (flags & MSG_CMSG_COMPAT)
79 + return -EINVAL;
80 + return __sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
81 }
82
83 asmlinkage long compat_sys_sendmmsg(int fd, struct compat_mmsghdr __user *mmsg,
84 unsigned vlen, unsigned int flags)
85 {
86 + if (flags & MSG_CMSG_COMPAT)
87 + return -EINVAL;
88 return __sys_sendmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
89 flags | MSG_CMSG_COMPAT);
90 }
91
92 asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, unsigned int flags)
93 {
94 - return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
95 + if (flags & MSG_CMSG_COMPAT)
96 + return -EINVAL;
97 + return __sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
98 }
99
100 asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags)
101 @@ -777,6 +783,9 @@ asmlinkage long compat_sys_recvmmsg(int
102 int datagrams;
103 struct timespec ktspec;
104
105 + if (flags & MSG_CMSG_COMPAT)
106 + return -EINVAL;
107 +
108 if (COMPAT_USE_64BIT_TIME)
109 return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
110 flags | MSG_CMSG_COMPAT,
111 --- a/net/socket.c
112 +++ b/net/socket.c
113 @@ -1899,9 +1899,9 @@ struct used_address {
114 unsigned int name_len;
115 };
116
117 -static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
118 - struct msghdr *msg_sys, unsigned flags,
119 - struct used_address *used_address)
120 +static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
121 + struct msghdr *msg_sys, unsigned flags,
122 + struct used_address *used_address)
123 {
124 struct compat_msghdr __user *msg_compat =
125 (struct compat_msghdr __user *)msg;
126 @@ -2017,22 +2017,30 @@ out:
127 * BSD sendmsg interface
128 */
129
130 -SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags)
131 +long __sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags)
132 {
133 int fput_needed, err;
134 struct msghdr msg_sys;
135 - struct socket *sock = sockfd_lookup_light(fd, &err, &fput_needed);
136 + struct socket *sock;
137
138 + sock = sockfd_lookup_light(fd, &err, &fput_needed);
139 if (!sock)
140 goto out;
141
142 - err = __sys_sendmsg(sock, msg, &msg_sys, flags, NULL);
143 + err = ___sys_sendmsg(sock, msg, &msg_sys, flags, NULL);
144
145 fput_light(sock->file, fput_needed);
146 out:
147 return err;
148 }
149
150 +SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned int, flags)
151 +{
152 + if (flags & MSG_CMSG_COMPAT)
153 + return -EINVAL;
154 + return __sys_sendmsg(fd, msg, flags);
155 +}
156 +
157 /*
158 * Linux sendmmsg interface
159 */
160 @@ -2063,15 +2071,16 @@ int __sys_sendmmsg(int fd, struct mmsghd
161
162 while (datagrams < vlen) {
163 if (MSG_CMSG_COMPAT & flags) {
164 - err = __sys_sendmsg(sock, (struct msghdr __user *)compat_entry,
165 - &msg_sys, flags, &used_address);
166 + err = ___sys_sendmsg(sock, (struct msghdr __user *)compat_entry,
167 + &msg_sys, flags, &used_address);
168 if (err < 0)
169 break;
170 err = __put_user(err, &compat_entry->msg_len);
171 ++compat_entry;
172 } else {
173 - err = __sys_sendmsg(sock, (struct msghdr __user *)entry,
174 - &msg_sys, flags, &used_address);
175 + err = ___sys_sendmsg(sock,
176 + (struct msghdr __user *)entry,
177 + &msg_sys, flags, &used_address);
178 if (err < 0)
179 break;
180 err = put_user(err, &entry->msg_len);
181 @@ -2095,11 +2104,13 @@ int __sys_sendmmsg(int fd, struct mmsghd
182 SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg,
183 unsigned int, vlen, unsigned int, flags)
184 {
185 + if (flags & MSG_CMSG_COMPAT)
186 + return -EINVAL;
187 return __sys_sendmmsg(fd, mmsg, vlen, flags);
188 }
189
190 -static int __sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
191 - struct msghdr *msg_sys, unsigned flags, int nosec)
192 +static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
193 + struct msghdr *msg_sys, unsigned flags, int nosec)
194 {
195 struct compat_msghdr __user *msg_compat =
196 (struct compat_msghdr __user *)msg;
197 @@ -2192,23 +2203,31 @@ out:
198 * BSD recvmsg interface
199 */
200
201 -SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg,
202 - unsigned int, flags)
203 +long __sys_recvmsg(int fd, struct msghdr __user *msg, unsigned flags)
204 {
205 int fput_needed, err;
206 struct msghdr msg_sys;
207 - struct socket *sock = sockfd_lookup_light(fd, &err, &fput_needed);
208 + struct socket *sock;
209
210 + sock = sockfd_lookup_light(fd, &err, &fput_needed);
211 if (!sock)
212 goto out;
213
214 - err = __sys_recvmsg(sock, msg, &msg_sys, flags, 0);
215 + err = ___sys_recvmsg(sock, msg, &msg_sys, flags, 0);
216
217 fput_light(sock->file, fput_needed);
218 out:
219 return err;
220 }
221
222 +SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg,
223 + unsigned int, flags)
224 +{
225 + if (flags & MSG_CMSG_COMPAT)
226 + return -EINVAL;
227 + return __sys_recvmsg(fd, msg, flags);
228 +}
229 +
230 /*
231 * Linux recvmmsg interface
232 */
233 @@ -2246,17 +2265,18 @@ int __sys_recvmmsg(int fd, struct mmsghd
234 * No need to ask LSM for more than the first datagram.
235 */
236 if (MSG_CMSG_COMPAT & flags) {
237 - err = __sys_recvmsg(sock, (struct msghdr __user *)compat_entry,
238 - &msg_sys, flags & ~MSG_WAITFORONE,
239 - datagrams);
240 + err = ___sys_recvmsg(sock, (struct msghdr __user *)compat_entry,
241 + &msg_sys, flags & ~MSG_WAITFORONE,
242 + datagrams);
243 if (err < 0)
244 break;
245 err = __put_user(err, &compat_entry->msg_len);
246 ++compat_entry;
247 } else {
248 - err = __sys_recvmsg(sock, (struct msghdr __user *)entry,
249 - &msg_sys, flags & ~MSG_WAITFORONE,
250 - datagrams);
251 + err = ___sys_recvmsg(sock,
252 + (struct msghdr __user *)entry,
253 + &msg_sys, flags & ~MSG_WAITFORONE,
254 + datagrams);
255 if (err < 0)
256 break;
257 err = put_user(err, &entry->msg_len);
258 @@ -2323,6 +2343,9 @@ SYSCALL_DEFINE5(recvmmsg, int, fd, struc
259 int datagrams;
260 struct timespec timeout_sys;
261
262 + if (flags & MSG_CMSG_COMPAT)
263 + return -EINVAL;
264 +
265 if (!timeout)
266 return __sys_recvmmsg(fd, mmsg, vlen, flags, NULL);
267