Skip to content

Commit 4284225

Browse files
Wen Gugregkh
Wen Gu
authored andcommitted
net/smc: Transitional solution for clcsock race issue
[ Upstream commit c0bf3d8 ] We encountered a crash in smc_setsockopt() and it is caused by accessing smc->clcsock after clcsock was released. BUG: kernel NULL pointer dereference, address: 0000000000000020 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: 0000 [#1] PREEMPT SMP PTI CPU: 1 PID: 50309 Comm: nginx Kdump: loaded Tainted: G E 5.16.0-rc4+ #53 RIP: 0010:smc_setsockopt+0x59/0x280 [smc] Call Trace: <TASK> __sys_setsockopt+0xfc/0x190 __x64_sys_setsockopt+0x20/0x30 do_syscall_64+0x34/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae RIP: 0033:0x7f16ba83918e </TASK> This patch tries to fix it by holding clcsock_release_lock and checking whether clcsock has already been released before access. In case that a crash of the same reason happens in smc_getsockopt() or smc_switch_to_fallback(), this patch also checkes smc->clcsock in them too. And the caller of smc_switch_to_fallback() will identify whether fallback succeeds according to the return value. Fixes: fd57770 ("net/smc: wait for pending work before clcsock release_sock") Link: https://lore.kernel.org/lkml/[email protected]/T/ Signed-off-by: Wen Gu <[email protected]> Acked-by: Karsten Graul <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent e3496f8 commit 4284225

File tree

1 file changed

+51
-12
lines changed

1 file changed

+51
-12
lines changed

net/smc/af_smc.c

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -566,12 +566,17 @@ static void smc_stat_fallback(struct smc_sock *smc)
566566
mutex_unlock(&net->smc.mutex_fback_rsn);
567567
}
568568

569-
static void smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
569+
static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
570570
{
571571
wait_queue_head_t *smc_wait = sk_sleep(&smc->sk);
572-
wait_queue_head_t *clc_wait = sk_sleep(smc->clcsock->sk);
572+
wait_queue_head_t *clc_wait;
573573
unsigned long flags;
574574

575+
mutex_lock(&smc->clcsock_release_lock);
576+
if (!smc->clcsock) {
577+
mutex_unlock(&smc->clcsock_release_lock);
578+
return -EBADF;
579+
}
575580
smc->use_fallback = true;
576581
smc->fallback_rsn = reason_code;
577582
smc_stat_fallback(smc);
@@ -586,18 +591,30 @@ static void smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
586591
* smc socket->wq, which should be removed
587592
* to clcsocket->wq during the fallback.
588593
*/
594+
clc_wait = sk_sleep(smc->clcsock->sk);
589595
spin_lock_irqsave(&smc_wait->lock, flags);
590596
spin_lock_nested(&clc_wait->lock, SINGLE_DEPTH_NESTING);
591597
list_splice_init(&smc_wait->head, &clc_wait->head);
592598
spin_unlock(&clc_wait->lock);
593599
spin_unlock_irqrestore(&smc_wait->lock, flags);
594600
}
601+
mutex_unlock(&smc->clcsock_release_lock);
602+
return 0;
595603
}
596604

597605
/* fall back during connect */
598606
static int smc_connect_fallback(struct smc_sock *smc, int reason_code)
599607
{
600-
smc_switch_to_fallback(smc, reason_code);
608+
struct net *net = sock_net(&smc->sk);
609+
int rc = 0;
610+
611+
rc = smc_switch_to_fallback(smc, reason_code);
612+
if (rc) { /* fallback fails */
613+
this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt);
614+
if (smc->sk.sk_state == SMC_INIT)
615+
sock_put(&smc->sk); /* passive closing */
616+
return rc;
617+
}
601618
smc_copy_sock_settings_to_clc(smc);
602619
smc->connect_nonblock = 0;
603620
if (smc->sk.sk_state == SMC_INIT)
@@ -1514,11 +1531,12 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
15141531
{
15151532
/* RDMA setup failed, switch back to TCP */
15161533
smc_conn_abort(new_smc, local_first);
1517-
if (reason_code < 0) { /* error, no fallback possible */
1534+
if (reason_code < 0 ||
1535+
smc_switch_to_fallback(new_smc, reason_code)) {
1536+
/* error, no fallback possible */
15181537
smc_listen_out_err(new_smc);
15191538
return;
15201539
}
1521-
smc_switch_to_fallback(new_smc, reason_code);
15221540
if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) {
15231541
if (smc_clc_send_decline(new_smc, reason_code, version) < 0) {
15241542
smc_listen_out_err(new_smc);
@@ -1960,8 +1978,11 @@ static void smc_listen_work(struct work_struct *work)
19601978

19611979
/* check if peer is smc capable */
19621980
if (!tcp_sk(newclcsock->sk)->syn_smc) {
1963-
smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC);
1964-
smc_listen_out_connected(new_smc);
1981+
rc = smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC);
1982+
if (rc)
1983+
smc_listen_out_err(new_smc);
1984+
else
1985+
smc_listen_out_connected(new_smc);
19651986
return;
19661987
}
19671988

@@ -2250,7 +2271,9 @@ static int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
22502271

22512272
if (msg->msg_flags & MSG_FASTOPEN) {
22522273
if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
2253-
smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2274+
rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2275+
if (rc)
2276+
goto out;
22542277
} else {
22552278
rc = -EINVAL;
22562279
goto out;
@@ -2443,6 +2466,11 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
24432466
/* generic setsockopts reaching us here always apply to the
24442467
* CLC socket
24452468
*/
2469+
mutex_lock(&smc->clcsock_release_lock);
2470+
if (!smc->clcsock) {
2471+
mutex_unlock(&smc->clcsock_release_lock);
2472+
return -EBADF;
2473+
}
24462474
if (unlikely(!smc->clcsock->ops->setsockopt))
24472475
rc = -EOPNOTSUPP;
24482476
else
@@ -2452,6 +2480,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
24522480
sk->sk_err = smc->clcsock->sk->sk_err;
24532481
sk_error_report(sk);
24542482
}
2483+
mutex_unlock(&smc->clcsock_release_lock);
24552484

24562485
if (optlen < sizeof(int))
24572486
return -EINVAL;
@@ -2468,7 +2497,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
24682497
case TCP_FASTOPEN_NO_COOKIE:
24692498
/* option not supported by SMC */
24702499
if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
2471-
smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2500+
rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
24722501
} else {
24732502
rc = -EINVAL;
24742503
}
@@ -2511,13 +2540,23 @@ static int smc_getsockopt(struct socket *sock, int level, int optname,
25112540
char __user *optval, int __user *optlen)
25122541
{
25132542
struct smc_sock *smc;
2543+
int rc;
25142544

25152545
smc = smc_sk(sock->sk);
2546+
mutex_lock(&smc->clcsock_release_lock);
2547+
if (!smc->clcsock) {
2548+
mutex_unlock(&smc->clcsock_release_lock);
2549+
return -EBADF;
2550+
}
25162551
/* socket options apply to the CLC socket */
2517-
if (unlikely(!smc->clcsock->ops->getsockopt))
2552+
if (unlikely(!smc->clcsock->ops->getsockopt)) {
2553+
mutex_unlock(&smc->clcsock_release_lock);
25182554
return -EOPNOTSUPP;
2519-
return smc->clcsock->ops->getsockopt(smc->clcsock, level, optname,
2520-
optval, optlen);
2555+
}
2556+
rc = smc->clcsock->ops->getsockopt(smc->clcsock, level, optname,
2557+
optval, optlen);
2558+
mutex_unlock(&smc->clcsock_release_lock);
2559+
return rc;
25212560
}
25222561

25232562
static int smc_ioctl(struct socket *sock, unsigned int cmd,

0 commit comments

Comments
 (0)