diff options
Diffstat (limited to 'net/compat.c')
| -rw-r--r-- | net/compat.c | 179 | 
1 files changed, 29 insertions, 150 deletions
diff --git a/net/compat.c b/net/compat.c index 5e3041a2c37d..95ce707a30a3 100644 --- a/net/compat.c +++ b/net/compat.c @@ -202,7 +202,7 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk,  		/* Advance. */  		kcmsg = (struct cmsghdr *)((char *)kcmsg + tmp); -		ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); +		ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, cmsg.cmsg_len);  	}  	/* @@ -281,39 +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; -	int err = 0, i; +	if (msg->msg_controllen <= sizeof(struct compat_cmsghdr)) +		return 0; +	return (msg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int); +} -	if (fdnum < fdmax) -		fdmax = fdnum; +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_COMPAT_DATA(cm); +	int err = 0, i; -	for (i = 0, cmfptr = (int __user *) CMSG_COMPAT_DATA(cm); i < fdmax; i++, cmfptr++) { -		int new_fd; -		err = security_file_receive(fp[i]); -		if (err) -			break; -		err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & kmsg->msg_flags -					  ? O_CLOEXEC : 0); +	for (i = 0; i < fdmax; i++) { +		err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags);  		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. */ -		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); @@ -321,134 +313,23 @@ 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);  } -/* allocate a 64-bit sock_fprog on the user stack for duration of syscall. */ -struct sock_fprog __user *get_compat_bpf_fprog(char __user *optval) -{ -	struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; -	struct sock_fprog __user *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog)); -	struct compat_sock_fprog f32; -	struct sock_fprog f; - -	if (copy_from_user(&f32, fprog32, sizeof(*fprog32))) -		return NULL; -	memset(&f, 0, sizeof(f)); -	f.len = f32.len; -	f.filter = compat_ptr(f32.filter); -	if (copy_to_user(kfprog, &f, sizeof(struct sock_fprog))) -		return NULL; - -	return kfprog; -} -EXPORT_SYMBOL_GPL(get_compat_bpf_fprog); - -static int do_set_attach_filter(struct socket *sock, int level, int optname, -				char __user *optval, unsigned int optlen) -{ -	struct sock_fprog __user *kfprog; - -	kfprog = get_compat_bpf_fprog(optval); -	if (!kfprog) -		return -EFAULT; - -	return sock_setsockopt(sock, level, optname, (char __user *)kfprog, -			      sizeof(struct sock_fprog)); -} - -static int compat_sock_setsockopt(struct socket *sock, int level, int optname, -				char __user *optval, unsigned int optlen) -{ -	if (optname == SO_ATTACH_FILTER || -	    optname == SO_ATTACH_REUSEPORT_CBPF) -		return do_set_attach_filter(sock, level, optname, -					    optval, optlen); -	return sock_setsockopt(sock, level, optname, optval, optlen); -} - -static int __compat_sys_setsockopt(int fd, int level, int optname, -				   char __user *optval, unsigned int optlen) -{ -	int err; -	struct socket *sock; - -	if (optlen > INT_MAX) -		return -EINVAL; - -	sock = sockfd_lookup(fd, &err); -	if (sock) { -		err = security_socket_setsockopt(sock, level, optname); -		if (err) { -			sockfd_put(sock); -			return err; -		} - -		if (level == SOL_SOCKET) -			err = compat_sock_setsockopt(sock, level, -					optname, optval, optlen); -		else if (sock->ops->compat_setsockopt) -			err = sock->ops->compat_setsockopt(sock, level, -					optname, optval, optlen); -		else -			err = sock->ops->setsockopt(sock, level, -					optname, optval, optlen); -		sockfd_put(sock); -	} -	return err; -} - -COMPAT_SYSCALL_DEFINE5(setsockopt, int, fd, int, level, int, optname, -		       char __user *, optval, unsigned int, optlen) -{ -	return __compat_sys_setsockopt(fd, level, optname, optval, optlen); -} - -static int __compat_sys_getsockopt(int fd, int level, int optname, -				   char __user *optval, -				   int __user *optlen) -{ -	int err; -	struct socket *sock = sockfd_lookup(fd, &err); - -	if (sock) { -		err = security_socket_getsockopt(sock, level, optname); -		if (err) { -			sockfd_put(sock); -			return err; -		} - -		if (level == SOL_SOCKET) -			err = sock_getsockopt(sock, level, -					optname, optval, optlen); -		else if (sock->ops->compat_getsockopt) -			err = sock->ops->compat_getsockopt(sock, level, -					optname, optval, optlen); -		else -			err = sock->ops->getsockopt(sock, level, -					optname, optval, optlen); -		sockfd_put(sock); -	} -	return err; -} - -COMPAT_SYSCALL_DEFINE5(getsockopt, int, fd, int, level, int, optname, -		       char __user *, optval, int __user *, optlen) -{ -	return __compat_sys_getsockopt(fd, level, optname, optval, optlen); -} -  /* Argument list sizes for compat_sys_socketcall */  #define AL(x) ((x) * sizeof(u32))  static unsigned char nas[21] = { @@ -608,13 +489,11 @@ COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args)  		ret = __sys_shutdown(a0, a1);  		break;  	case SYS_SETSOCKOPT: -		ret = __compat_sys_setsockopt(a0, a1, a[2], -					      compat_ptr(a[3]), a[4]); +		ret = __sys_setsockopt(a0, a1, a[2], compat_ptr(a[3]), a[4]);  		break;  	case SYS_GETSOCKOPT: -		ret = __compat_sys_getsockopt(a0, a1, a[2], -					      compat_ptr(a[3]), -					      compat_ptr(a[4])); +		ret = __sys_getsockopt(a0, a1, a[2], compat_ptr(a[3]), +				       compat_ptr(a[4]));  		break;  	case SYS_SENDMSG:  		ret = __compat_sys_sendmsg(a0, compat_ptr(a1), a[2]);  | 
