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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2020-01-29 17:45:20 +01:00
commit d55c6ab4b3
6 changed files with 46 additions and 25 deletions

View File

@ -148,9 +148,7 @@ struct tcp_request_sock {
const struct tcp_request_sock_ops *af_specific;
u64 snt_synack; /* first SYNACK sent time */
bool tfo_listener;
#if IS_ENABLED(CONFIG_MPTCP)
bool is_mptcp;
#endif
u32 txhash;
u32 rcv_isn;
u32 snt_isn;

View File

@ -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;
treq->snt_synack = 0;
treq->tfo_listener = false;
if (IS_ENABLED(CONFIG_MPTCP))
treq->is_mptcp = 0;
if (IS_ENABLED(CONFIG_SMC))
ireq->smc_ok = 0;

View File

@ -6637,6 +6637,9 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
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))
goto drop_and_free;

View File

@ -178,6 +178,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
treq = tcp_rsk(req);
treq->tfo_listener = false;
if (IS_ENABLED(CONFIG_MPTCP))
treq->is_mptcp = 0;
if (security_inet_conn_request(sk, skb, req))
goto out_free;

View File

@ -644,19 +644,21 @@ static void __mptcp_close(struct sock *sk, long timeout)
{
struct mptcp_subflow_context *subflow, *tmp;
struct mptcp_sock *msk = mptcp_sk(sk);
LIST_HEAD(conn_list);
mptcp_token_destroy(msk->token);
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);
__mptcp_close_ssk(sk, ssk, subflow, timeout);
}
if (msk->cached_ext)
__skb_ext_put(msk->cached_ext);
release_sock(sk);
sk_common_release(sk);
}
@ -776,18 +778,19 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
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,
char __user *uoptval, unsigned int optlen)
char __user *optval, unsigned int optlen)
{
struct mptcp_sock *msk = mptcp_sk(sk);
char __kernel *optval;
int ret = -EOPNOTSUPP;
struct socket *ssock;
/* will be treated as __user in tcp_setsockopt */
optval = (char __kernel __force *)uoptval;
struct sock *ssk;
pr_debug("msk=%p", msk);
@ -796,27 +799,28 @@ static int mptcp_setsockopt(struct sock *sk, int level, int optname,
*/
lock_sock(sk);
ssock = __mptcp_socket_create(msk, MPTCP_SAME_STATE);
if (!IS_ERR(ssock)) {
pr_debug("subflow=%p", ssock->sk);
ret = kernel_setsockopt(ssock, level, optname, optval, optlen);
if (IS_ERR(ssock)) {
release_sock(sk);
return ret;
}
ssk = ssock->sk;
sock_hold(ssk);
release_sock(sk);
ret = tcp_setsockopt(ssk, level, optname, optval, optlen);
sock_put(ssk);
return ret;
}
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);
char __kernel *optval;
int ret = -EOPNOTSUPP;
int __kernel *option;
struct socket *ssock;
/* will be treated as __user in tcp_getsockopt */
optval = (char __kernel __force *)uoptval;
option = (int __kernel __force *)uoption;
struct sock *ssk;
pr_debug("msk=%p", msk);
@ -825,12 +829,18 @@ static int mptcp_getsockopt(struct sock *sk, int level, int optname,
*/
lock_sock(sk);
ssock = __mptcp_socket_create(msk, MPTCP_SAME_STATE);
if (!IS_ERR(ssock)) {
pr_debug("subflow=%p", ssock->sk);
ret = kernel_getsockopt(ssock, level, optname, optval, option);
if (IS_ERR(ssock)) {
release_sock(sk);
return ret;
}
ssk = ssock->sk;
sock_hold(ssk);
release_sock(sk);
ret = tcp_getsockopt(ssk, level, optname, optval, option);
sock_put(ssk);
return ret;
}

View File

@ -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);
if (tcp_rsk(req)->is_mptcp == 0)
goto create_child;
/* if the sk is MP_CAPABLE, we try to fetch the client key */
subflow_req = mptcp_subflow_rsk(req);
if (subflow_req->mp_capable) {
@ -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 *new_ctx;
if (!subflow_req->mp_capable) {
if (!tcp_rsk(req)->is_mptcp || !subflow_req->mp_capable) {
subflow_ulp_fallback(newsk, old_ctx);
return;
}