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

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
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -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;
+4 −0
Original line number Diff line number Diff line
@@ -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;

+3 −0
Original line number Diff line number Diff line
@@ -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;

+3 −0
Original line number Diff line number Diff line
@@ -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;

+32 −22
Original line number Diff line number Diff line
@@ -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;
}

Loading