forked from mirrors/linux
		
	bpf: net: Change sk_getsockopt() to take the sockptr_t argument
This patch changes sk_getsockopt() to take the sockptr_t argument such that it can be used by bpf_getsockopt(SOL_SOCKET) in a latter patch. security_socket_getpeersec_stream() is not changed. It stays with the __user ptr (optval.user and optlen.user) to avoid changes to other security hooks. bpf_getsockopt(SOL_SOCKET) also does not support SO_PEERSEC. Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org> Link: https://lore.kernel.org/r/20220902002802.2888419-1-kafai@fb.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
		
							parent
							
								
									ba74a7608d
								
							
						
					
					
						commit
						4ff09db1b7
					
				
					 4 changed files with 32 additions and 24 deletions
				
			
		|  | @ -900,8 +900,7 @@ int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk); | ||||||
| int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk); | int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk); | ||||||
| void sk_reuseport_prog_free(struct bpf_prog *prog); | void sk_reuseport_prog_free(struct bpf_prog *prog); | ||||||
| int sk_detach_filter(struct sock *sk); | int sk_detach_filter(struct sock *sk); | ||||||
| int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, | int sk_get_filter(struct sock *sk, sockptr_t optval, unsigned int len); | ||||||
| 		  unsigned int len); |  | ||||||
| 
 | 
 | ||||||
| bool sk_filter_charge(struct sock *sk, struct sk_filter *fp); | bool sk_filter_charge(struct sock *sk, struct sk_filter *fp); | ||||||
| void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp); | void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp); | ||||||
|  |  | ||||||
|  | @ -64,6 +64,11 @@ static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline int copy_to_sockptr(sockptr_t dst, const void *src, size_t size) | ||||||
|  | { | ||||||
|  | 	return copy_to_sockptr_offset(dst, 0, src, size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline void *memdup_sockptr(sockptr_t src, size_t len) | static inline void *memdup_sockptr(sockptr_t src, size_t len) | ||||||
| { | { | ||||||
| 	void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN); | 	void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN); | ||||||
|  |  | ||||||
|  | @ -10716,8 +10716,7 @@ int sk_detach_filter(struct sock *sk) | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(sk_detach_filter); | EXPORT_SYMBOL_GPL(sk_detach_filter); | ||||||
| 
 | 
 | ||||||
| int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, | int sk_get_filter(struct sock *sk, sockptr_t optval, unsigned int len) | ||||||
| 		  unsigned int len) |  | ||||||
| { | { | ||||||
| 	struct sock_fprog_kern *fprog; | 	struct sock_fprog_kern *fprog; | ||||||
| 	struct sk_filter *filter; | 	struct sk_filter *filter; | ||||||
|  | @ -10748,7 +10747,7 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
| 	ret = -EFAULT; | 	ret = -EFAULT; | ||||||
| 	if (copy_to_user(ubuf, fprog->filter, bpf_classic_proglen(fprog))) | 	if (copy_to_sockptr(optval, fprog->filter, bpf_classic_proglen(fprog))) | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
| 	/* Instead of bytes, the API requests to return the number
 | 	/* Instead of bytes, the API requests to return the number
 | ||||||
|  |  | ||||||
|  | @ -712,8 +712,8 @@ static int sock_setbindtodevice(struct sock *sk, sockptr_t optval, int optlen) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int sock_getbindtodevice(struct sock *sk, char __user *optval, | static int sock_getbindtodevice(struct sock *sk, sockptr_t optval, | ||||||
| 				int __user *optlen, int len) | 				sockptr_t optlen, int len) | ||||||
| { | { | ||||||
| 	int ret = -ENOPROTOOPT; | 	int ret = -ENOPROTOOPT; | ||||||
| #ifdef CONFIG_NETDEVICES | #ifdef CONFIG_NETDEVICES | ||||||
|  | @ -737,12 +737,12 @@ static int sock_getbindtodevice(struct sock *sk, char __user *optval, | ||||||
| 	len = strlen(devname) + 1; | 	len = strlen(devname) + 1; | ||||||
| 
 | 
 | ||||||
| 	ret = -EFAULT; | 	ret = -EFAULT; | ||||||
| 	if (copy_to_user(optval, devname, len)) | 	if (copy_to_sockptr(optval, devname, len)) | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
| zero: | zero: | ||||||
| 	ret = -EFAULT; | 	ret = -EFAULT; | ||||||
| 	if (put_user(len, optlen)) | 	if (copy_to_sockptr(optlen, &len, sizeof(int))) | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
| 	ret = 0; | 	ret = 0; | ||||||
|  | @ -1568,20 +1568,23 @@ static void cred_to_ucred(struct pid *pid, const struct cred *cred, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int groups_to_user(gid_t __user *dst, const struct group_info *src) | static int groups_to_user(sockptr_t dst, const struct group_info *src) | ||||||
| { | { | ||||||
| 	struct user_namespace *user_ns = current_user_ns(); | 	struct user_namespace *user_ns = current_user_ns(); | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < src->ngroups; i++) | 	for (i = 0; i < src->ngroups; i++) { | ||||||
| 		if (put_user(from_kgid_munged(user_ns, src->gid[i]), dst + i)) | 		gid_t gid = from_kgid_munged(user_ns, src->gid[i]); | ||||||
|  | 
 | ||||||
|  | 		if (copy_to_sockptr_offset(dst, i * sizeof(gid), &gid, sizeof(gid))) | ||||||
| 			return -EFAULT; | 			return -EFAULT; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int sk_getsockopt(struct sock *sk, int level, int optname, | static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 			 char __user *optval, int __user *optlen) | 			 sockptr_t optval, sockptr_t optlen) | ||||||
| { | { | ||||||
| 	struct socket *sock = sk->sk_socket; | 	struct socket *sock = sk->sk_socket; | ||||||
| 
 | 
 | ||||||
|  | @ -1600,7 +1603,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 	int lv = sizeof(int); | 	int lv = sizeof(int); | ||||||
| 	int len; | 	int len; | ||||||
| 
 | 
 | ||||||
| 	if (get_user(len, optlen)) | 	if (copy_from_sockptr(&len, optlen, sizeof(int))) | ||||||
| 		return -EFAULT; | 		return -EFAULT; | ||||||
| 	if (len < 0) | 	if (len < 0) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | @ -1735,7 +1738,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 		cred_to_ucred(sk->sk_peer_pid, sk->sk_peer_cred, &peercred); | 		cred_to_ucred(sk->sk_peer_pid, sk->sk_peer_cred, &peercred); | ||||||
| 		spin_unlock(&sk->sk_peer_lock); | 		spin_unlock(&sk->sk_peer_lock); | ||||||
| 
 | 
 | ||||||
| 		if (copy_to_user(optval, &peercred, len)) | 		if (copy_to_sockptr(optval, &peercred, len)) | ||||||
| 			return -EFAULT; | 			return -EFAULT; | ||||||
| 		goto lenout; | 		goto lenout; | ||||||
| 	} | 	} | ||||||
|  | @ -1753,11 +1756,11 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 		if (len < n * sizeof(gid_t)) { | 		if (len < n * sizeof(gid_t)) { | ||||||
| 			len = n * sizeof(gid_t); | 			len = n * sizeof(gid_t); | ||||||
| 			put_cred(cred); | 			put_cred(cred); | ||||||
| 			return put_user(len, optlen) ? -EFAULT : -ERANGE; | 			return copy_to_sockptr(optlen, &len, sizeof(int)) ? -EFAULT : -ERANGE; | ||||||
| 		} | 		} | ||||||
| 		len = n * sizeof(gid_t); | 		len = n * sizeof(gid_t); | ||||||
| 
 | 
 | ||||||
| 		ret = groups_to_user((gid_t __user *)optval, cred->group_info); | 		ret = groups_to_user(optval, cred->group_info); | ||||||
| 		put_cred(cred); | 		put_cred(cred); | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			return ret; | 			return ret; | ||||||
|  | @ -1773,7 +1776,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 			return -ENOTCONN; | 			return -ENOTCONN; | ||||||
| 		if (lv < len) | 		if (lv < len) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		if (copy_to_user(optval, address, len)) | 		if (copy_to_sockptr(optval, address, len)) | ||||||
| 			return -EFAULT; | 			return -EFAULT; | ||||||
| 		goto lenout; | 		goto lenout; | ||||||
| 	} | 	} | ||||||
|  | @ -1790,7 +1793,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case SO_PEERSEC: | 	case SO_PEERSEC: | ||||||
| 		return security_socket_getpeersec_stream(sock, optval, optlen, len); | 		return security_socket_getpeersec_stream(sock, optval.user, optlen.user, len); | ||||||
| 
 | 
 | ||||||
| 	case SO_MARK: | 	case SO_MARK: | ||||||
| 		v.val = sk->sk_mark; | 		v.val = sk->sk_mark; | ||||||
|  | @ -1822,7 +1825,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 		return sock_getbindtodevice(sk, optval, optlen, len); | 		return sock_getbindtodevice(sk, optval, optlen, len); | ||||||
| 
 | 
 | ||||||
| 	case SO_GET_FILTER: | 	case SO_GET_FILTER: | ||||||
| 		len = sk_get_filter(sk, (struct sock_filter __user *)optval, len); | 		len = sk_get_filter(sk, optval, len); | ||||||
| 		if (len < 0) | 		if (len < 0) | ||||||
| 			return len; | 			return len; | ||||||
| 
 | 
 | ||||||
|  | @ -1870,7 +1873,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 		sk_get_meminfo(sk, meminfo); | 		sk_get_meminfo(sk, meminfo); | ||||||
| 
 | 
 | ||||||
| 		len = min_t(unsigned int, len, sizeof(meminfo)); | 		len = min_t(unsigned int, len, sizeof(meminfo)); | ||||||
| 		if (copy_to_user(optval, &meminfo, len)) | 		if (copy_to_sockptr(optval, &meminfo, len)) | ||||||
| 			return -EFAULT; | 			return -EFAULT; | ||||||
| 
 | 
 | ||||||
| 		goto lenout; | 		goto lenout; | ||||||
|  | @ -1939,10 +1942,10 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 
 | 
 | ||||||
| 	if (len > lv) | 	if (len > lv) | ||||||
| 		len = lv; | 		len = lv; | ||||||
| 	if (copy_to_user(optval, &v, len)) | 	if (copy_to_sockptr(optval, &v, len)) | ||||||
| 		return -EFAULT; | 		return -EFAULT; | ||||||
| lenout: | lenout: | ||||||
| 	if (put_user(len, optlen)) | 	if (copy_to_sockptr(optlen, &len, sizeof(int))) | ||||||
| 		return -EFAULT; | 		return -EFAULT; | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | @ -1950,7 +1953,9 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, | ||||||
| int sock_getsockopt(struct socket *sock, int level, int optname, | int sock_getsockopt(struct socket *sock, int level, int optname, | ||||||
| 		    char __user *optval, int __user *optlen) | 		    char __user *optval, int __user *optlen) | ||||||
| { | { | ||||||
| 	return sk_getsockopt(sock->sk, level, optname, optval, optlen); | 	return sk_getsockopt(sock->sk, level, optname, | ||||||
|  | 			     USER_SOCKPTR(optval), | ||||||
|  | 			     USER_SOCKPTR(optlen)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Martin KaFai Lau
						Martin KaFai Lau