forked from mirrors/linux
		
	net/scm: Regularize compat handling of scm_detach_fds()
Duplicate the cleanups from commit2618d530dd("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 commit1f466e1f15("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 commit6900317f5e("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 <hch@lst.de> Cc: Sargun Dhillon <sargun@sargun.me> Cc: Jakub Kicinski <kuba@kernel.org> Cc: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Acked-by: Christian Brauner <christian.brauner@ubuntu.com> Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
		
							parent
							
								
									4969f8a073
								
							
						
					
					
						commit
						c0029de509
					
				
					 3 changed files with 37 additions and 47 deletions
				
			
		|  | @ -37,6 +37,7 @@ struct scm_cookie { | |||
| #endif | ||||
| }; | ||||
| 
 | ||||
| int __scm_install_fd(struct file *file, int __user *ufd, unsigned int o_flags); | ||||
| void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm); | ||||
| void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm); | ||||
| int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm); | ||||
|  |  | |||
							
								
								
									
										56
									
								
								net/compat.c
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								net/compat.c
									
									
									
									
									
								
							|  | @ -281,40 +281,31 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) | ||||
| static int scm_max_fds_compat(struct msghdr *msg) | ||||
| { | ||||
| 	struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; | ||||
| 	int fdmax = (kmsg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int); | ||||
| 	int fdnum = scm->fp->count; | ||||
| 	struct file **fp = scm->fp->fp; | ||||
| 	int __user *cmfptr; | ||||
| 	if (msg->msg_controllen <= sizeof(struct compat_cmsghdr)) | ||||
| 		return 0; | ||||
| 	return (msg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int); | ||||
| } | ||||
| 
 | ||||
| void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm) | ||||
| { | ||||
| 	struct compat_cmsghdr __user *cm = | ||||
| 		(struct compat_cmsghdr __user *)msg->msg_control; | ||||
| 	unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; | ||||
| 	int fdmax = min_t(int, scm_max_fds_compat(msg), scm->fp->count); | ||||
| 	int __user *cmsg_data = CMSG_USER_DATA(cm); | ||||
| 	int err = 0, i; | ||||
| 
 | ||||
| 	if (fdnum < fdmax) | ||||
| 		fdmax = fdnum; | ||||
| 
 | ||||
| 	for (i = 0, cmfptr = (int __user *) CMSG_COMPAT_DATA(cm); i < fdmax; i++, cmfptr++) { | ||||
| 		int new_fd; | ||||
| 		err = security_file_receive(fp[i]); | ||||
| 	for (i = 0; i < fdmax; i++) { | ||||
| 		err = __scm_install_fd(scm->fp->fp[i], cmsg_data + i, o_flags); | ||||
| 		if (err) | ||||
| 			break; | ||||
| 		err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & kmsg->msg_flags | ||||
| 					  ? O_CLOEXEC : 0); | ||||
| 		if (err < 0) | ||||
| 			break; | ||||
| 		new_fd = err; | ||||
| 		err = put_user(new_fd, cmfptr); | ||||
| 		if (err) { | ||||
| 			put_unused_fd(new_fd); | ||||
| 			break; | ||||
| 		} | ||||
| 		/* Bump the usage count and install the file. */ | ||||
| 		__receive_sock(fp[i]); | ||||
| 		fd_install(new_fd, get_file(fp[i])); | ||||
| 	} | ||||
| 
 | ||||
| 	if (i > 0) { | ||||
| 		int cmlen = CMSG_COMPAT_LEN(i * sizeof(int)); | ||||
| 
 | ||||
| 		err = put_user(SOL_SOCKET, &cm->cmsg_level); | ||||
| 		if (!err) | ||||
| 			err = put_user(SCM_RIGHTS, &cm->cmsg_type); | ||||
|  | @ -322,16 +313,19 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) | |||
| 			err = put_user(cmlen, &cm->cmsg_len); | ||||
| 		if (!err) { | ||||
| 			cmlen = CMSG_COMPAT_SPACE(i * sizeof(int)); | ||||
| 			kmsg->msg_control += cmlen; | ||||
| 			kmsg->msg_controllen -= cmlen; | ||||
| 			if (msg->msg_controllen < cmlen) | ||||
| 				cmlen = msg->msg_controllen; | ||||
| 			msg->msg_control += cmlen; | ||||
| 			msg->msg_controllen -= cmlen; | ||||
| 		} | ||||
| 	} | ||||
| 	if (i < fdnum) | ||||
| 		kmsg->msg_flags |= MSG_CTRUNC; | ||||
| 
 | ||||
| 	if (i < scm->fp->count || (scm->fp->count && fdmax <= 0)) | ||||
| 		msg->msg_flags |= MSG_CTRUNC; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * All of the files that fit in the message have had their | ||||
| 	 * usage counts incremented, so we just free the list. | ||||
| 	 * All of the files that fit in the message have had their usage counts | ||||
| 	 * incremented, so we just free the list. | ||||
| 	 */ | ||||
| 	__scm_destroy(scm); | ||||
| } | ||||
|  |  | |||
|  | @ -280,9 +280,8 @@ void put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_inter | |||
| } | ||||
| EXPORT_SYMBOL(put_cmsg_scm_timestamping); | ||||
| 
 | ||||
| static int __scm_install_fd(struct file *file, int __user *ufd, int o_flags) | ||||
| int __scm_install_fd(struct file *file, int __user *ufd, unsigned int o_flags) | ||||
| { | ||||
| 	struct socket *sock; | ||||
| 	int new_fd; | ||||
| 	int error; | ||||
| 
 | ||||
|  | @ -300,12 +299,8 @@ static int __scm_install_fd(struct file *file, int __user *ufd, int o_flags) | |||
| 		return error; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Bump the usage count and install the file. */ | ||||
| 	sock = sock_from_file(file, &error); | ||||
| 	if (sock) { | ||||
| 		sock_update_netprioidx(&sock->sk->sk_cgrp_data); | ||||
| 		sock_update_classid(&sock->sk->sk_cgrp_data); | ||||
| 	} | ||||
| 	/* Bump the sock usage counts, if any. */ | ||||
| 	__receive_sock(file); | ||||
| 	fd_install(new_fd, get_file(file)); | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -319,29 +314,29 @@ static int scm_max_fds(struct msghdr *msg) | |||
| 
 | ||||
| void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) | ||||
| { | ||||
| 	struct cmsghdr __user *cm | ||||
| 		= (__force struct cmsghdr __user*)msg->msg_control; | ||||
| 	int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; | ||||
| 	struct cmsghdr __user *cm = | ||||
| 		(__force struct cmsghdr __user *)msg->msg_control; | ||||
| 	unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; | ||||
| 	int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count); | ||||
| 	int __user *cmsg_data = CMSG_USER_DATA(cm); | ||||
| 	int err = 0, i; | ||||
| 
 | ||||
| 	/* no use for FD passing from kernel space callers */ | ||||
| 	if (WARN_ON_ONCE(!msg->msg_control_is_user)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (msg->msg_flags & MSG_CMSG_COMPAT) { | ||||
| 		scm_detach_fds_compat(msg, scm); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* no use for FD passing from kernel space callers */ | ||||
| 	if (WARN_ON_ONCE(!msg->msg_control_is_user)) | ||||
| 		return; | ||||
| 
 | ||||
| 	for (i = 0; i < fdmax; i++) { | ||||
| 		err = __scm_install_fd(scm->fp->fp[i], cmsg_data + i, o_flags); | ||||
| 		if (err) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (i > 0)  { | ||||
| 	if (i > 0) { | ||||
| 		int cmlen = CMSG_LEN(i * sizeof(int)); | ||||
| 
 | ||||
| 		err = put_user(SOL_SOCKET, &cm->cmsg_level); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Kees Cook
						Kees Cook