aboutsummaryrefslogtreecommitdiff
path: root/net/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/socket.c')
-rw-r--r--net/socket.c146
1 files changed, 73 insertions, 73 deletions
diff --git a/net/socket.c b/net/socket.c
index 976426d03f09..dbbe8ea7d395 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -500,7 +500,7 @@ static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed)
if (f.file) {
sock = sock_from_file(f.file, err);
if (likely(sock)) {
- *fput_needed = f.flags;
+ *fput_needed = f.flags & FDPUT_FPUT;
return sock;
}
fdput(f);
@@ -586,15 +586,6 @@ struct socket *sock_alloc(void)
}
EXPORT_SYMBOL(sock_alloc);
-/**
- * sock_release - close a socket
- * @sock: socket to close
- *
- * The socket is released from the protocol stack if it has a release
- * callback, and the inode is then released if the socket is bound to
- * an inode not a file.
- */
-
static void __sock_release(struct socket *sock, struct inode *inode)
{
if (sock->ops) {
@@ -620,6 +611,14 @@ static void __sock_release(struct socket *sock, struct inode *inode)
sock->file = NULL;
}
+/**
+ * sock_release - close a socket
+ * @sock: socket to close
+ *
+ * The socket is released from the protocol stack if it has a release
+ * callback, and the inode is then released if the socket is bound to
+ * an inode not a file.
+ */
void sock_release(struct socket *sock)
{
__sock_release(sock, NULL);
@@ -1326,7 +1325,7 @@ int sock_wake_async(struct socket_wq *wq, int how, int band)
case SOCK_WAKE_SPACE:
if (!test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags))
break;
- /* fall through */
+ fallthrough;
case SOCK_WAKE_IO:
call_kill:
kill_fasync(&wq->fasync_list, SIGIO, band);
@@ -1805,8 +1804,7 @@ int __sys_accept4(int fd, struct sockaddr __user *upeer_sockaddr,
ret = __sys_accept4_file(f.file, 0, upeer_sockaddr,
upeer_addrlen, flags,
rlimit(RLIMIT_NOFILE));
- if (f.flags)
- fput(f.file);
+ fdput(f);
}
return ret;
@@ -1869,8 +1867,7 @@ int __sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)
ret = move_addr_to_kernel(uservaddr, addrlen, &address);
if (!ret)
ret = __sys_connect_file(f.file, &address, addrlen, 0);
- if (f.flags)
- fput(f.file);
+ fdput(f);
}
return ret;
@@ -2080,15 +2077,25 @@ SYSCALL_DEFINE4(recv, int, fd, void __user *, ubuf, size_t, size,
return __sys_recvfrom(fd, ubuf, size, flags, NULL, NULL);
}
+static bool sock_use_custom_sol_socket(const struct socket *sock)
+{
+ const struct sock *sk = sock->sk;
+
+ /* Use sock->ops->setsockopt() for MPTCP */
+ return IS_ENABLED(CONFIG_MPTCP) &&
+ sk->sk_protocol == IPPROTO_MPTCP &&
+ sk->sk_type == SOCK_STREAM &&
+ (sk->sk_family == AF_INET || sk->sk_family == AF_INET6);
+}
+
/*
* Set a socket option. Because we don't know the option lengths we have
* to pass the user mode parameter for the protocols to sort out.
*/
-
-static int __sys_setsockopt(int fd, int level, int optname,
- char __user *optval, int optlen)
+int __sys_setsockopt(int fd, int level, int optname, char __user *user_optval,
+ int optlen)
{
- mm_segment_t oldfs = get_fs();
+ sockptr_t optval = USER_SOCKPTR(user_optval);
char *kernel_optval = NULL;
int err, fput_needed;
struct socket *sock;
@@ -2097,43 +2104,36 @@ static int __sys_setsockopt(int fd, int level, int optname,
return -EINVAL;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
- if (sock != NULL) {
- 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);
+ if (!sock)
+ return err;
- if (err < 0) {
- goto out_put;
- } else if (err > 0) {
- err = 0;
- goto out_put;
- }
+ err = security_socket_setsockopt(sock, level, optname);
+ if (err)
+ goto out_put;
- if (kernel_optval) {
- set_fs(KERNEL_DS);
- optval = (char __user __force *)kernel_optval;
- }
+ if (!in_compat_syscall())
+ err = BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock->sk, &level, &optname,
+ user_optval, &optlen,
+ &kernel_optval);
+ if (err < 0)
+ goto out_put;
+ if (err > 0) {
+ err = 0;
+ goto out_put;
+ }
- if (level == SOL_SOCKET)
- err =
- sock_setsockopt(sock, level, optname, optval,
+ if (kernel_optval)
+ optval = KERNEL_SOCKPTR(kernel_optval);
+ if (level == SOL_SOCKET && !sock_use_custom_sol_socket(sock))
+ err = sock_setsockopt(sock, level, optname, optval, optlen);
+ else if (unlikely(!sock->ops->setsockopt))
+ err = -EOPNOTSUPP;
+ else
+ err = sock->ops->setsockopt(sock, level, optname, optval,
optlen);
- else
- err =
- sock->ops->setsockopt(sock, level, optname, optval,
- optlen);
-
- if (kernel_optval) {
- set_fs(oldfs);
- kfree(kernel_optval);
- }
+ kfree(kernel_optval);
out_put:
- fput_light(sock->file, fput_needed);
- }
+ fput_light(sock->file, fput_needed);
return err;
}
@@ -2147,37 +2147,38 @@ SYSCALL_DEFINE5(setsockopt, int, fd, int, level, int, optname,
* Get a socket option. Because we don't know the option lengths we have
* to pass a user mode parameter for the protocols to sort out.
*/
-
-static int __sys_getsockopt(int fd, int level, int optname,
- char __user *optval, int __user *optlen)
+int __sys_getsockopt(int fd, int level, int optname, char __user *optval,
+ int __user *optlen)
{
int err, fput_needed;
struct socket *sock;
int max_optlen;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
- if (sock != NULL) {
- err = security_socket_getsockopt(sock, level, optname);
- if (err)
- goto out_put;
+ if (!sock)
+ return err;
+
+ err = security_socket_getsockopt(sock, level, optname);
+ if (err)
+ goto out_put;
+ if (!in_compat_syscall())
max_optlen = BPF_CGROUP_GETSOCKOPT_MAX_OPTLEN(optlen);
- if (level == SOL_SOCKET)
- err =
- sock_getsockopt(sock, level, optname, optval,
+ if (level == SOL_SOCKET)
+ err = sock_getsockopt(sock, level, optname, optval, optlen);
+ else if (unlikely(!sock->ops->getsockopt))
+ err = -EOPNOTSUPP;
+ else
+ err = sock->ops->getsockopt(sock, level, optname, optval,
optlen);
- else
- err =
- sock->ops->getsockopt(sock, level, optname, optval,
- optlen);
+ if (!in_compat_syscall())
err = BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock->sk, level, optname,
- optval, optlen,
- max_optlen, err);
+ optval, optlen, max_optlen,
+ err);
out_put:
- fput_light(sock->file, fput_needed);
- }
+ fput_light(sock->file, fput_needed);
return err;
}
@@ -3058,7 +3059,7 @@ static int __init sock_init(void)
err = register_filesystem(&sock_fs_type);
if (err)
- goto out_fs;
+ goto out;
sock_mnt = kern_mount(&sock_fs_type);
if (IS_ERR(sock_mnt)) {
err = PTR_ERR(sock_mnt);
@@ -3081,7 +3082,6 @@ out:
out_mount:
unregister_filesystem(&sock_fs_type);
-out_fs:
goto out;
}
@@ -3154,13 +3154,13 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
if (rule_cnt > KMALLOC_MAX_SIZE / sizeof(u32))
return -ENOMEM;
buf_size += rule_cnt * sizeof(u32);
- /* fall through */
+ fallthrough;
case ETHTOOL_GRXRINGS:
case ETHTOOL_GRXCLSRLCNT:
case ETHTOOL_GRXCLSRULE:
case ETHTOOL_SRXCLSRLINS:
convert_out = true;
- /* fall through */
+ fallthrough;
case ETHTOOL_SRXCLSRLDEL:
buf_size += sizeof(struct ethtool_rxnfc);
convert_in = true;