Skip to content

Commit 1f466e1

Browse files
Christoph Hellwigdavem330
authored andcommitted
net: cleanly handle kernel vs user buffers for ->msg_control
The msg_control field in struct msghdr can either contain a user pointer when used with the recvmsg system call, or a kernel pointer when used with sendmsg. To complicate things further kernel_recvmsg can stuff a kernel pointer in and then use set_fs to make the uaccess helpers accept it. Replace it with a union of a kernel pointer msg_control field, and a user pointer msg_control_user one, and allow kernel_recvmsg operate on a proper kernel pointer using a bitfield to override the normal choice of a user pointer for recvmsg. Signed-off-by: Christoph Hellwig <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2618d53 commit 1f466e1

File tree

5 files changed

+50
-41
lines changed

5 files changed

+50
-41
lines changed

include/linux/socket.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,17 @@ struct msghdr {
5050
void *msg_name; /* ptr to socket address structure */
5151
int msg_namelen; /* size of socket address structure */
5252
struct iov_iter msg_iter; /* data */
53-
void *msg_control; /* ancillary data */
53+
54+
/*
55+
* Ancillary data. msg_control_user is the user buffer used for the
56+
* recv* side when msg_control_is_user is set, msg_control is the kernel
57+
* buffer used for all other cases.
58+
*/
59+
union {
60+
void *msg_control;
61+
void __user *msg_control_user;
62+
};
63+
bool msg_control_is_user : 1;
5464
__kernel_size_t msg_controllen; /* ancillary data buffer length */
5565
unsigned int msg_flags; /* flags on received message */
5666
struct kiocb *msg_iocb; /* ptr to iocb for async requests */

net/compat.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ int __get_compat_msghdr(struct msghdr *kmsg,
5656
if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
5757
kmsg->msg_namelen = sizeof(struct sockaddr_storage);
5858

59-
kmsg->msg_control = compat_ptr(msg.msg_control);
59+
kmsg->msg_control_is_user = true;
60+
kmsg->msg_control_user = compat_ptr(msg.msg_control);
6061
kmsg->msg_controllen = msg.msg_controllen;
6162

6263
if (save_addr)
@@ -121,7 +122,7 @@ int get_compat_msghdr(struct msghdr *kmsg,
121122
((ucmlen) >= sizeof(struct compat_cmsghdr) && \
122123
(ucmlen) <= (unsigned long) \
123124
((mhdr)->msg_controllen - \
124-
((char *)(ucmsg) - (char *)(mhdr)->msg_control)))
125+
((char __user *)(ucmsg) - (char __user *)(mhdr)->msg_control_user)))
125126

126127
static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *msg,
127128
struct compat_cmsghdr __user *cmsg, int cmsg_len)

net/core/scm.c

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -212,40 +212,43 @@ EXPORT_SYMBOL(__scm_send);
212212

213213
int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
214214
{
215-
struct cmsghdr __user *cm
216-
= (__force struct cmsghdr __user *)msg->msg_control;
217-
struct cmsghdr cmhdr;
218215
int cmlen = CMSG_LEN(len);
219-
int err;
220216

221-
if (MSG_CMSG_COMPAT & msg->msg_flags)
217+
if (msg->msg_flags & MSG_CMSG_COMPAT)
222218
return put_cmsg_compat(msg, level, type, len, data);
223219

224-
if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
220+
if (!msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) {
225221
msg->msg_flags |= MSG_CTRUNC;
226222
return 0; /* XXX: return error? check spec. */
227223
}
228224
if (msg->msg_controllen < cmlen) {
229225
msg->msg_flags |= MSG_CTRUNC;
230226
cmlen = msg->msg_controllen;
231227
}
232-
cmhdr.cmsg_level = level;
233-
cmhdr.cmsg_type = type;
234-
cmhdr.cmsg_len = cmlen;
235-
236-
err = -EFAULT;
237-
if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
238-
goto out;
239-
if (copy_to_user(CMSG_USER_DATA(cm), data, cmlen - sizeof(*cm)))
240-
goto out;
241-
cmlen = CMSG_SPACE(len);
242-
if (msg->msg_controllen < cmlen)
243-
cmlen = msg->msg_controllen;
228+
229+
if (msg->msg_control_is_user) {
230+
struct cmsghdr __user *cm = msg->msg_control_user;
231+
struct cmsghdr cmhdr;
232+
233+
cmhdr.cmsg_level = level;
234+
cmhdr.cmsg_type = type;
235+
cmhdr.cmsg_len = cmlen;
236+
if (copy_to_user(cm, &cmhdr, sizeof cmhdr) ||
237+
copy_to_user(CMSG_USER_DATA(cm), data, cmlen - sizeof(*cm)))
238+
return -EFAULT;
239+
} else {
240+
struct cmsghdr *cm = msg->msg_control;
241+
242+
cm->cmsg_level = level;
243+
cm->cmsg_type = type;
244+
cm->cmsg_len = cmlen;
245+
memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm));
246+
}
247+
248+
cmlen = min(CMSG_SPACE(len), msg->msg_controllen);
244249
msg->msg_control += cmlen;
245250
msg->msg_controllen -= cmlen;
246-
err = 0;
247-
out:
248-
return err;
251+
return 0;
249252
}
250253
EXPORT_SYMBOL(put_cmsg);
251254

@@ -328,6 +331,10 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
328331
return;
329332
}
330333

334+
/* no use for FD passing from kernel space callers */
335+
if (WARN_ON_ONCE(!msg->msg_control_is_user))
336+
return;
337+
331338
for (i = 0; i < fdmax; i++) {
332339
err = __scm_install_fd(scm->fp->fp[i], cmsg_data + i, o_flags);
333340
if (err)

net/ipv4/ip_sockglue.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1492,7 +1492,8 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
14921492
if (sk->sk_type != SOCK_STREAM)
14931493
return -ENOPROTOOPT;
14941494

1495-
msg.msg_control = (__force void *) optval;
1495+
msg.msg_control_is_user = true;
1496+
msg.msg_control_user = optval;
14961497
msg.msg_controllen = len;
14971498
msg.msg_flags = flags;
14981499

net/socket.c

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -924,14 +924,9 @@ EXPORT_SYMBOL(sock_recvmsg);
924924
int kernel_recvmsg(struct socket *sock, struct msghdr *msg,
925925
struct kvec *vec, size_t num, size_t size, int flags)
926926
{
927-
mm_segment_t oldfs = get_fs();
928-
int result;
929-
927+
msg->msg_control_is_user = false;
930928
iov_iter_kvec(&msg->msg_iter, READ, vec, num, size);
931-
set_fs(KERNEL_DS);
932-
result = sock_recvmsg(sock, msg, flags);
933-
set_fs(oldfs);
934-
return result;
929+
return sock_recvmsg(sock, msg, flags);
935930
}
936931
EXPORT_SYMBOL(kernel_recvmsg);
937932

@@ -2239,7 +2234,8 @@ int __copy_msghdr_from_user(struct msghdr *kmsg,
22392234
if (copy_from_user(&msg, umsg, sizeof(*umsg)))
22402235
return -EFAULT;
22412236

2242-
kmsg->msg_control = (void __force *)msg.msg_control;
2237+
kmsg->msg_control_is_user = true;
2238+
kmsg->msg_control_user = msg.msg_control;
22432239
kmsg->msg_controllen = msg.msg_controllen;
22442240
kmsg->msg_flags = msg.msg_flags;
22452241

@@ -2331,16 +2327,10 @@ static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
23312327
goto out;
23322328
}
23332329
err = -EFAULT;
2334-
/*
2335-
* Careful! Before this, msg_sys->msg_control contains a user pointer.
2336-
* Afterwards, it will be a kernel pointer. Thus the compiler-assisted
2337-
* checking falls down on this.
2338-
*/
2339-
if (copy_from_user(ctl_buf,
2340-
(void __user __force *)msg_sys->msg_control,
2341-
ctl_len))
2330+
if (copy_from_user(ctl_buf, msg_sys->msg_control_user, ctl_len))
23422331
goto out_freectl;
23432332
msg_sys->msg_control = ctl_buf;
2333+
msg_sys->msg_control_is_user = false;
23442334
}
23452335
msg_sys->msg_flags = flags;
23462336

0 commit comments

Comments
 (0)