Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8065cde authored by D. Wythe's avatar D. Wythe Committed by Greg Kroah-Hartman
Browse files

net/smc: fix dangling sock under state SMC_APPFINCLOSEWAIT



[ Upstream commit 5211c9729484c923f8d2e06bd29f9322cc42bb8f ]

Considering scenario:

				smc_cdc_rx_handler
__smc_release
				sock_set_flag
smc_close_active()
sock_set_flag

__set_bit(DEAD)			__set_bit(DONE)

Dues to __set_bit is not atomic, the DEAD or DONE might be lost.
if the DEAD flag lost, the state SMC_CLOSED  will be never be reached
in smc_close_passive_work:

if (sock_flag(sk, SOCK_DEAD) &&
	smc_close_sent_any_close(conn)) {
	sk->sk_state = SMC_CLOSED;
} else {
	/* just shutdown, but not yet closed locally */
	sk->sk_state = SMC_APPFINCLOSEWAIT;
}

Replace sock_set_flags or __set_bit to set_bit will fix this problem.
Since set_bit is atomic.

Fixes: b38d7324 ("smc: socket closing and linkgroup cleanup")
Signed-off-by: default avatarD. Wythe <alibuda@linux.alibaba.com>
Reviewed-by: default avatarDust Li <dust.li@linux.alibaba.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 592f934b
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -136,7 +136,7 @@ static int __smc_release(struct smc_sock *smc)

	if (!smc->use_fallback) {
		rc = smc_close_active(smc);
		sock_set_flag(sk, SOCK_DEAD);
		smc_sock_set_flag(sk, SOCK_DEAD);
		sk->sk_shutdown |= SHUTDOWN_MASK;
	} else {
		if (sk->sk_state != SMC_CLOSED) {
@@ -928,7 +928,7 @@ static int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
		if (new_clcsock)
			sock_release(new_clcsock);
		new_sk->sk_state = SMC_CLOSED;
		sock_set_flag(new_sk, SOCK_DEAD);
		smc_sock_set_flag(new_sk, SOCK_DEAD);
		sock_put(new_sk); /* final */
		*new_smc = NULL;
		goto out;
+5 −0
Original line number Diff line number Diff line
@@ -265,4 +265,9 @@ static inline bool using_ipsec(struct smc_sock *smc)
struct sock *smc_accept_dequeue(struct sock *parent, struct socket *new_sock);
void smc_close_non_accepted(struct sock *sk);

static inline void smc_sock_set_flag(struct sock *sk, enum sock_flags flag)
{
	set_bit(flag, &sk->sk_flags);
}

#endif	/* __SMC_H */
+1 −1
Original line number Diff line number Diff line
@@ -298,7 +298,7 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
		smc->sk.sk_shutdown |= RCV_SHUTDOWN;
		if (smc->clcsock && smc->clcsock->sk)
			smc->clcsock->sk->sk_shutdown |= RCV_SHUTDOWN;
		sock_set_flag(&smc->sk, SOCK_DONE);
		smc_sock_set_flag(&smc->sk, SOCK_DONE);
		sock_hold(&smc->sk); /* sock_put in close_work */
		if (!schedule_work(&conn->close_work))
			sock_put(&smc->sk);
+1 −1
Original line number Diff line number Diff line
@@ -164,7 +164,7 @@ static void smc_close_active_abort(struct smc_sock *smc)
		break;
	}

	sock_set_flag(sk, SOCK_DEAD);
	smc_sock_set_flag(sk, SOCK_DEAD);
	sk->sk_state_change(sk);
}