Skip to content

Commit c0bf3d8

Browse files
Wen Gudavem330
Wen Gu
authored andcommitted
net/smc: Transitional solution for clcsock race issue
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]>
1 parent 3a5d9db commit c0bf3d8

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)
@@ -1518,11 +1535,12 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
15181535
{
15191536
/* RDMA setup failed, switch back to TCP */
15201537
smc_conn_abort(new_smc, local_first);
1521-
if (reason_code < 0) { /* error, no fallback possible */
1538+
if (reason_code < 0 ||
1539+
smc_switch_to_fallback(new_smc, reason_code)) {
1540+
/* error, no fallback possible */
15221541
smc_listen_out_err(new_smc);
15231542
return;
15241543
}
1525-
smc_switch_to_fallback(new_smc, reason_code);
15261544
if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) {
15271545
if (smc_clc_send_decline(new_smc, reason_code, version) < 0) {
15281546
smc_listen_out_err(new_smc);
@@ -1964,8 +1982,11 @@ static void smc_listen_work(struct work_struct *work)
19641982

19651983
/* check if peer is smc capable */
19661984
if (!tcp_sk(newclcsock->sk)->syn_smc) {
1967-
smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC);
1968-
smc_listen_out_connected(new_smc);
1985+
rc = smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC);
1986+
if (rc)
1987+
smc_listen_out_err(new_smc);
1988+
else
1989+
smc_listen_out_connected(new_smc);
19691990
return;
19701991
}
19711992

@@ -2254,7 +2275,9 @@ static int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
22542275

22552276
if (msg->msg_flags & MSG_FASTOPEN) {
22562277
if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
2257-
smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2278+
rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2279+
if (rc)
2280+
goto out;
22582281
} else {
22592282
rc = -EINVAL;
22602283
goto out;
@@ -2447,6 +2470,11 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
24472470
/* generic setsockopts reaching us here always apply to the
24482471
* CLC socket
24492472
*/
2473+
mutex_lock(&smc->clcsock_release_lock);
2474+
if (!smc->clcsock) {
2475+
mutex_unlock(&smc->clcsock_release_lock);
2476+
return -EBADF;
2477+
}
24502478
if (unlikely(!smc->clcsock->ops->setsockopt))
24512479
rc = -EOPNOTSUPP;
24522480
else
@@ -2456,6 +2484,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
24562484
sk->sk_err = smc->clcsock->sk->sk_err;
24572485
sk_error_report(sk);
24582486
}
2487+
mutex_unlock(&smc->clcsock_release_lock);
24592488

24602489
if (optlen < sizeof(int))
24612490
return -EINVAL;
@@ -2472,7 +2501,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
24722501
case TCP_FASTOPEN_NO_COOKIE:
24732502
/* option not supported by SMC */
24742503
if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
2475-
smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2504+
rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
24762505
} else {
24772506
rc = -EINVAL;
24782507
}
@@ -2515,13 +2544,23 @@ static int smc_getsockopt(struct socket *sock, int level, int optname,
25152544
char __user *optval, int __user *optlen)
25162545
{
25172546
struct smc_sock *smc;
2547+
int rc;
25182548

25192549
smc = smc_sk(sock->sk);
2550+
mutex_lock(&smc->clcsock_release_lock);
2551+
if (!smc->clcsock) {
2552+
mutex_unlock(&smc->clcsock_release_lock);
2553+
return -EBADF;
2554+
}
25202555
/* socket options apply to the CLC socket */
2521-
if (unlikely(!smc->clcsock->ops->getsockopt))
2556+
if (unlikely(!smc->clcsock->ops->getsockopt)) {
2557+
mutex_unlock(&smc->clcsock_release_lock);
25222558
return -EOPNOTSUPP;
2523-
return smc->clcsock->ops->getsockopt(smc->clcsock, level, optname,
2524-
optval, optlen);
2559+
}
2560+
rc = smc->clcsock->ops->getsockopt(smc->clcsock, level, optname,
2561+
optval, optlen);
2562+
mutex_unlock(&smc->clcsock_release_lock);
2563+
return rc;
25252564
}
25262565

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

0 commit comments

Comments
 (0)