summaryrefslogtreecommitdiff
path: root/drivers/net/ovpn/tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ovpn/tcp.c')
-rw-r--r--drivers/net/ovpn/tcp.c23
1 files changed, 20 insertions, 3 deletions
diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c
index 0d7f30360d87..ec2bbc28c196 100644
--- a/drivers/net/ovpn/tcp.c
+++ b/drivers/net/ovpn/tcp.c
@@ -199,7 +199,19 @@ void ovpn_tcp_socket_detach(struct ovpn_socket *ovpn_sock)
sk->sk_data_ready = peer->tcp.sk_cb.sk_data_ready;
sk->sk_write_space = peer->tcp.sk_cb.sk_write_space;
sk->sk_prot = peer->tcp.sk_cb.prot;
- sk->sk_socket->ops = peer->tcp.sk_cb.ops;
+
+ /* tcp_close() may race this function and could set
+ * sk->sk_socket to NULL. It does so by invoking
+ * sock_orphan(), which holds sk_callback_lock before
+ * doing the assignment.
+ *
+ * For this reason we acquire the same lock to avoid
+ * sk_socket to disappear under our feet
+ */
+ write_lock_bh(&sk->sk_callback_lock);
+ if (sk->sk_socket)
+ sk->sk_socket->ops = peer->tcp.sk_cb.ops;
+ write_unlock_bh(&sk->sk_callback_lock);
rcu_assign_sk_user_data(sk, NULL);
}
@@ -487,6 +499,7 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
/* make sure no pre-existing encapsulation handler exists */
if (ovpn_sock->sk->sk_user_data)
return -EBUSY;
+ rcu_assign_sk_user_data(ovpn_sock->sk, ovpn_sock);
/* only a fully connected socket is expected. Connection should be
* handled in userspace
@@ -495,13 +508,14 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
net_err_ratelimited("%s: provided TCP socket is not in ESTABLISHED state: %d\n",
netdev_name(peer->ovpn->dev),
ovpn_sock->sk->sk_state);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
ret = strp_init(&peer->tcp.strp, ovpn_sock->sk, &cb);
if (ret < 0) {
DEBUG_NET_WARN_ON_ONCE(1);
- return ret;
+ goto err;
}
INIT_WORK(&peer->tcp.defer_del_work, ovpn_tcp_peer_del_work);
@@ -536,6 +550,9 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
strp_check_rcv(&peer->tcp.strp);
return 0;
+err:
+ rcu_assign_sk_user_data(ovpn_sock->sk, NULL);
+ return ret;
}
static void ovpn_tcp_close(struct sock *sk, long timeout)