Skip to content

Commit c0029de

Browse files
committed
net/scm: Regularize compat handling of scm_detach_fds()
Duplicate the cleanups from commit 2618d53 ("net/scm: cleanup scm_detach_fds") into the compat code. Replace open-coded __receive_sock() with a call to the helper. Move the check added in commit 1f466e1 ("net: cleanly handle kernel vs user buffers for ->msg_control") to before the compat call, even though it should be impossible for an in-kernel call to also be compat. Correct the int "flags" argument to unsigned int to match fd_install() and similar APIs. Regularize any remaining differences, including a whitespace issue, a checkpatch warning, and add the check from commit 6900317 ("net, scm: fix PaX detected msg_controllen overflow in scm_detach_fds") which fixed an overflow unique to 64-bit. To avoid confusion when comparing the compat handler to the native handler, just include the same check in the compat handler. Cc: Christoph Hellwig <[email protected]> Cc: Sargun Dhillon <[email protected]> Cc: Jakub Kicinski <[email protected]> Cc: [email protected] Cc: [email protected] Acked-by: Christian Brauner <[email protected]> Signed-off-by: Kees Cook <[email protected]>
1 parent 4969f8a commit c0029de

File tree

3 files changed

+37
-47
lines changed

3 files changed

+37
-47
lines changed

include/net/scm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct scm_cookie {
3737
#endif
3838
};
3939

40+
int __scm_install_fd(struct file *file, int __user *ufd, unsigned int o_flags);
4041
void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm);
4142
void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm);
4243
int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm);

net/compat.c

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -281,57 +281,51 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat
281281
return 0;
282282
}
283283

284-
void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm)
284+
static int scm_max_fds_compat(struct msghdr *msg)
285285
{
286-
struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control;
287-
int fdmax = (kmsg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int);
288-
int fdnum = scm->fp->count;
289-
struct file **fp = scm->fp->fp;
290-
int __user *cmfptr;
291-
int err = 0, i;
286+
if (msg->msg_controllen <= sizeof(struct compat_cmsghdr))
287+
return 0;
288+
return (msg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int);
289+
}
292290

293-
if (fdnum < fdmax)
294-
fdmax = fdnum;
291+
void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm)
292+
{
293+
struct compat_cmsghdr __user *cm =
294+
(struct compat_cmsghdr __user *)msg->msg_control;
295+
unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
296+
int fdmax = min_t(int, scm_max_fds_compat(msg), scm->fp->count);
297+
int __user *cmsg_data = CMSG_USER_DATA(cm);
298+
int err = 0, i;
295299

296-
for (i = 0, cmfptr = (int __user *) CMSG_COMPAT_DATA(cm); i < fdmax; i++, cmfptr++) {
297-
int new_fd;
298-
err = security_file_receive(fp[i]);
300+
for (i = 0; i < fdmax; i++) {
301+
err = __scm_install_fd(scm->fp->fp[i], cmsg_data + i, o_flags);
299302
if (err)
300303
break;
301-
err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & kmsg->msg_flags
302-
? O_CLOEXEC : 0);
303-
if (err < 0)
304-
break;
305-
new_fd = err;
306-
err = put_user(new_fd, cmfptr);
307-
if (err) {
308-
put_unused_fd(new_fd);
309-
break;
310-
}
311-
/* Bump the usage count and install the file. */
312-
__receive_sock(fp[i]);
313-
fd_install(new_fd, get_file(fp[i]));
314304
}
315305

316306
if (i > 0) {
317307
int cmlen = CMSG_COMPAT_LEN(i * sizeof(int));
308+
318309
err = put_user(SOL_SOCKET, &cm->cmsg_level);
319310
if (!err)
320311
err = put_user(SCM_RIGHTS, &cm->cmsg_type);
321312
if (!err)
322313
err = put_user(cmlen, &cm->cmsg_len);
323314
if (!err) {
324315
cmlen = CMSG_COMPAT_SPACE(i * sizeof(int));
325-
kmsg->msg_control += cmlen;
326-
kmsg->msg_controllen -= cmlen;
316+
if (msg->msg_controllen < cmlen)
317+
cmlen = msg->msg_controllen;
318+
msg->msg_control += cmlen;
319+
msg->msg_controllen -= cmlen;
327320
}
328321
}
329-
if (i < fdnum)
330-
kmsg->msg_flags |= MSG_CTRUNC;
322+
323+
if (i < scm->fp->count || (scm->fp->count && fdmax <= 0))
324+
msg->msg_flags |= MSG_CTRUNC;
331325

332326
/*
333-
* All of the files that fit in the message have had their
334-
* usage counts incremented, so we just free the list.
327+
* All of the files that fit in the message have had their usage counts
328+
* incremented, so we just free the list.
335329
*/
336330
__scm_destroy(scm);
337331
}

net/core/scm.c

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,8 @@ void put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_inter
280280
}
281281
EXPORT_SYMBOL(put_cmsg_scm_timestamping);
282282

283-
static int __scm_install_fd(struct file *file, int __user *ufd, int o_flags)
283+
int __scm_install_fd(struct file *file, int __user *ufd, unsigned int o_flags)
284284
{
285-
struct socket *sock;
286285
int new_fd;
287286
int error;
288287

@@ -300,12 +299,8 @@ static int __scm_install_fd(struct file *file, int __user *ufd, int o_flags)
300299
return error;
301300
}
302301

303-
/* Bump the usage count and install the file. */
304-
sock = sock_from_file(file, &error);
305-
if (sock) {
306-
sock_update_netprioidx(&sock->sk->sk_cgrp_data);
307-
sock_update_classid(&sock->sk->sk_cgrp_data);
308-
}
302+
/* Bump the sock usage counts, if any. */
303+
__receive_sock(file);
309304
fd_install(new_fd, get_file(file));
310305
return 0;
311306
}
@@ -319,29 +314,29 @@ static int scm_max_fds(struct msghdr *msg)
319314

320315
void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
321316
{
322-
struct cmsghdr __user *cm
323-
= (__force struct cmsghdr __user*)msg->msg_control;
324-
int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
317+
struct cmsghdr __user *cm =
318+
(__force struct cmsghdr __user *)msg->msg_control;
319+
unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
325320
int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count);
326321
int __user *cmsg_data = CMSG_USER_DATA(cm);
327322
int err = 0, i;
328323

324+
/* no use for FD passing from kernel space callers */
325+
if (WARN_ON_ONCE(!msg->msg_control_is_user))
326+
return;
327+
329328
if (msg->msg_flags & MSG_CMSG_COMPAT) {
330329
scm_detach_fds_compat(msg, scm);
331330
return;
332331
}
333332

334-
/* no use for FD passing from kernel space callers */
335-
if (WARN_ON_ONCE(!msg->msg_control_is_user))
336-
return;
337-
338333
for (i = 0; i < fdmax; i++) {
339334
err = __scm_install_fd(scm->fp->fp[i], cmsg_data + i, o_flags);
340335
if (err)
341336
break;
342337
}
343338

344-
if (i > 0) {
339+
if (i > 0) {
345340
int cmlen = CMSG_LEN(i * sizeof(int));
346341

347342
err = put_user(SOL_SOCKET, &cm->cmsg_level);

0 commit comments

Comments
 (0)