net/socket.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/net/socket.c b/net/socket.c index 976426d03f09..39060a5f0caa 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2086,25 +2086,37 @@ SYSCALL_DEFINE4(recv, int, fd, void __user *, ubuf, size_t, size, */ static int __sys_setsockopt(int fd, int level, int optname, - char __user *optval, int optlen) + char __user *u_optval, int optlen) { - mm_segment_t oldfs = get_fs(); - char *kernel_optval = NULL; int err, fput_needed; struct socket *sock; + char *optval; if (optlen < 0) return -EINVAL; + /* Can we have some upper limit on optlen */ + optval = kmalloc(optlen, GFP_KERNEL | __GFP_NOWARN); + if (!optval) + return -ENOMEM; + + if (copy_from_user(optval, u_optval, optlen)) { + kfree(optval); + return -EFAULT; + } + sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock != NULL) { + char *bpf_optval = NULL; + char *use_optval; + err = security_socket_setsockopt(sock, level, optname); if (err) goto out_put; err = BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock->sk, &level, &optname, optval, &optlen, - &kernel_optval); + &bpf_optval); if (err < 0) { goto out_put; @@ -2113,27 +2125,23 @@ static int __sys_setsockopt(int fd, int level, int optname, goto out_put; } - if (kernel_optval) { - set_fs(KERNEL_DS); - optval = (char __user __force *)kernel_optval; - } + use_optval = bpf_optval ? bpf_optval : optval; if (level == SOL_SOCKET) err = - sock_setsockopt(sock, level, optname, optval, + sock_setsockopt(sock, level, optname, use_optval, optlen); else err = - sock->ops->setsockopt(sock, level, optname, optval, + sock->ops->setsockopt(sock, level, optname, use_optval, optlen); - if (kernel_optval) { - set_fs(oldfs); - kfree(kernel_optval); - } + if (bpf_optval) + kfree(bpf_optval); out_put: fput_light(sock->file, fput_needed); } + kfree(optval); return err; }