Commit d55c6ab4 authored by David S. Miller's avatar David S. Miller

Merge branch 'mptcp-fix-sockopt-crash-and-lockdep-splats'

Florian Westphal says:

====================
mptcp: fix sockopt crash and lockdep splats

Christoph Paasch reported a few bugs and lockdep splats triggered by
syzkaller.

One patch fixes a crash in set/getsockopt.
Two patches fix lockdep splats related to the order in which RTNL
and socket lock are taken.

Last patch fixes out-of-bounds access when TCP syncookies are used.

Change since last iteration on mptcp-list:
 - add needed refcount in patch 2
 - call tcp_get/setsockopt directly in patch 2

 Other patches unchanged except minor amends to commit messages.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 44efc78d ae2dd716
...@@ -148,9 +148,7 @@ struct tcp_request_sock { ...@@ -148,9 +148,7 @@ struct tcp_request_sock {
const struct tcp_request_sock_ops *af_specific; const struct tcp_request_sock_ops *af_specific;
u64 snt_synack; /* first SYNACK sent time */ u64 snt_synack; /* first SYNACK sent time */
bool tfo_listener; bool tfo_listener;
#if IS_ENABLED(CONFIG_MPTCP)
bool is_mptcp; bool is_mptcp;
#endif
u32 txhash; u32 txhash;
u32 rcv_isn; u32 rcv_isn;
u32 snt_isn; u32 snt_isn;
......
...@@ -349,6 +349,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ...@@ -349,6 +349,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
treq->snt_synack = 0; treq->snt_synack = 0;
treq->tfo_listener = false; treq->tfo_listener = false;
if (IS_ENABLED(CONFIG_MPTCP))
treq->is_mptcp = 0;
if (IS_ENABLED(CONFIG_SMC)) if (IS_ENABLED(CONFIG_SMC))
ireq->smc_ok = 0; ireq->smc_ok = 0;
......
...@@ -6637,6 +6637,9 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, ...@@ -6637,6 +6637,9 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
af_ops->init_req(req, sk, skb); af_ops->init_req(req, sk, skb);
if (IS_ENABLED(CONFIG_MPTCP) && want_cookie)
tcp_rsk(req)->is_mptcp = 0;
if (security_inet_conn_request(sk, skb, req)) if (security_inet_conn_request(sk, skb, req))
goto drop_and_free; goto drop_and_free;
......
...@@ -178,6 +178,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ...@@ -178,6 +178,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
treq = tcp_rsk(req); treq = tcp_rsk(req);
treq->tfo_listener = false; treq->tfo_listener = false;
if (IS_ENABLED(CONFIG_MPTCP))
treq->is_mptcp = 0;
if (security_inet_conn_request(sk, skb, req)) if (security_inet_conn_request(sk, skb, req))
goto out_free; goto out_free;
......
...@@ -644,19 +644,21 @@ static void __mptcp_close(struct sock *sk, long timeout) ...@@ -644,19 +644,21 @@ static void __mptcp_close(struct sock *sk, long timeout)
{ {
struct mptcp_subflow_context *subflow, *tmp; struct mptcp_subflow_context *subflow, *tmp;
struct mptcp_sock *msk = mptcp_sk(sk); struct mptcp_sock *msk = mptcp_sk(sk);
LIST_HEAD(conn_list);
mptcp_token_destroy(msk->token); mptcp_token_destroy(msk->token);
inet_sk_state_store(sk, TCP_CLOSE); inet_sk_state_store(sk, TCP_CLOSE);
list_for_each_entry_safe(subflow, tmp, &msk->conn_list, node) { list_splice_init(&msk->conn_list, &conn_list);
release_sock(sk);
list_for_each_entry_safe(subflow, tmp, &conn_list, node) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow); struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
__mptcp_close_ssk(sk, ssk, subflow, timeout); __mptcp_close_ssk(sk, ssk, subflow, timeout);
} }
if (msk->cached_ext)
__skb_ext_put(msk->cached_ext);
release_sock(sk);
sk_common_release(sk); sk_common_release(sk);
} }
...@@ -776,18 +778,19 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err, ...@@ -776,18 +778,19 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
static void mptcp_destroy(struct sock *sk) static void mptcp_destroy(struct sock *sk)
{ {
struct mptcp_sock *msk = mptcp_sk(sk);
if (msk->cached_ext)
__skb_ext_put(msk->cached_ext);
} }
static int mptcp_setsockopt(struct sock *sk, int level, int optname, static int mptcp_setsockopt(struct sock *sk, int level, int optname,
char __user *uoptval, unsigned int optlen) char __user *optval, unsigned int optlen)
{ {
struct mptcp_sock *msk = mptcp_sk(sk); struct mptcp_sock *msk = mptcp_sk(sk);
char __kernel *optval;
int ret = -EOPNOTSUPP; int ret = -EOPNOTSUPP;
struct socket *ssock; struct socket *ssock;
struct sock *ssk;
/* will be treated as __user in tcp_setsockopt */
optval = (char __kernel __force *)uoptval;
pr_debug("msk=%p", msk); pr_debug("msk=%p", msk);
...@@ -796,27 +799,28 @@ static int mptcp_setsockopt(struct sock *sk, int level, int optname, ...@@ -796,27 +799,28 @@ static int mptcp_setsockopt(struct sock *sk, int level, int optname,
*/ */
lock_sock(sk); lock_sock(sk);
ssock = __mptcp_socket_create(msk, MPTCP_SAME_STATE); ssock = __mptcp_socket_create(msk, MPTCP_SAME_STATE);
if (!IS_ERR(ssock)) { if (IS_ERR(ssock)) {
pr_debug("subflow=%p", ssock->sk); release_sock(sk);
ret = kernel_setsockopt(ssock, level, optname, optval, optlen); return ret;
} }
ssk = ssock->sk;
sock_hold(ssk);
release_sock(sk); release_sock(sk);
ret = tcp_setsockopt(ssk, level, optname, optval, optlen);
sock_put(ssk);
return ret; return ret;
} }
static int mptcp_getsockopt(struct sock *sk, int level, int optname, static int mptcp_getsockopt(struct sock *sk, int level, int optname,
char __user *uoptval, int __user *uoption) char __user *optval, int __user *option)
{ {
struct mptcp_sock *msk = mptcp_sk(sk); struct mptcp_sock *msk = mptcp_sk(sk);
char __kernel *optval;
int ret = -EOPNOTSUPP; int ret = -EOPNOTSUPP;
int __kernel *option;
struct socket *ssock; struct socket *ssock;
struct sock *ssk;
/* will be treated as __user in tcp_getsockopt */
optval = (char __kernel __force *)uoptval;
option = (int __kernel __force *)uoption;
pr_debug("msk=%p", msk); pr_debug("msk=%p", msk);
...@@ -825,12 +829,18 @@ static int mptcp_getsockopt(struct sock *sk, int level, int optname, ...@@ -825,12 +829,18 @@ static int mptcp_getsockopt(struct sock *sk, int level, int optname,
*/ */
lock_sock(sk); lock_sock(sk);
ssock = __mptcp_socket_create(msk, MPTCP_SAME_STATE); ssock = __mptcp_socket_create(msk, MPTCP_SAME_STATE);
if (!IS_ERR(ssock)) { if (IS_ERR(ssock)) {
pr_debug("subflow=%p", ssock->sk); release_sock(sk);
ret = kernel_getsockopt(ssock, level, optname, optval, option); return ret;
} }
ssk = ssock->sk;
sock_hold(ssk);
release_sock(sk); release_sock(sk);
ret = tcp_getsockopt(ssk, level, optname, optval, option);
sock_put(ssk);
return ret; return ret;
} }
......
...@@ -186,6 +186,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, ...@@ -186,6 +186,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
pr_debug("listener=%p, req=%p, conn=%p", listener, req, listener->conn); pr_debug("listener=%p, req=%p, conn=%p", listener, req, listener->conn);
if (tcp_rsk(req)->is_mptcp == 0)
goto create_child;
/* if the sk is MP_CAPABLE, we try to fetch the client key */ /* if the sk is MP_CAPABLE, we try to fetch the client key */
subflow_req = mptcp_subflow_rsk(req); subflow_req = mptcp_subflow_rsk(req);
if (subflow_req->mp_capable) { if (subflow_req->mp_capable) {
...@@ -769,7 +772,7 @@ static void subflow_ulp_clone(const struct request_sock *req, ...@@ -769,7 +772,7 @@ static void subflow_ulp_clone(const struct request_sock *req,
struct mptcp_subflow_context *old_ctx = mptcp_subflow_ctx(newsk); struct mptcp_subflow_context *old_ctx = mptcp_subflow_ctx(newsk);
struct mptcp_subflow_context *new_ctx; struct mptcp_subflow_context *new_ctx;
if (!subflow_req->mp_capable) { if (!tcp_rsk(req)->is_mptcp || !subflow_req->mp_capable) {
subflow_ulp_fallback(newsk, old_ctx); subflow_ulp_fallback(newsk, old_ctx);
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment