Skip to content

Commit 38f0bdd

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 fdcdc94 commit 38f0bdd

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
@@ -548,12 +548,17 @@ static void smc_stat_fallback(struct smc_sock *smc)
548548
mutex_unlock(&net->smc.mutex_fback_rsn);
549549
}
550550

551-
static void smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
551+
static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
552552
{
553553
wait_queue_head_t *smc_wait = sk_sleep(&smc->sk);
554-
wait_queue_head_t *clc_wait = sk_sleep(smc->clcsock->sk);
554+
wait_queue_head_t *clc_wait;
555555
unsigned long flags;
556556

557+
mutex_lock(&smc->clcsock_release_lock);
558+
if (!smc->clcsock) {
559+
mutex_unlock(&smc->clcsock_release_lock);
560+
return -EBADF;
561+
}
557562
smc->use_fallback = true;
558563
smc->fallback_rsn = reason_code;
559564
smc_stat_fallback(smc);
@@ -567,18 +572,30 @@ static void smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
567572
* smc socket->wq, which should be removed
568573
* to clcsocket->wq during the fallback.
569574
*/
575+
clc_wait = sk_sleep(smc->clcsock->sk);
570576
spin_lock_irqsave(&smc_wait->lock, flags);
571577
spin_lock_nested(&clc_wait->lock, SINGLE_DEPTH_NESTING);
572578
list_splice_init(&smc_wait->head, &clc_wait->head);
573579
spin_unlock(&clc_wait->lock);
574580
spin_unlock_irqrestore(&smc_wait->lock, flags);
575581
}
582+
mutex_unlock(&smc->clcsock_release_lock);
583+
return 0;
576584
}
577585

578586
/* fall back during connect */
579587
static int smc_connect_fallback(struct smc_sock *smc, int reason_code)
580588
{
581-
smc_switch_to_fallback(smc, reason_code);
589+
struct net *net = sock_net(&smc->sk);
590+
int rc = 0;
591+
592+
rc = smc_switch_to_fallback(smc, reason_code);
593+
if (rc) { /* fallback fails */
594+
this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt);
595+
if (smc->sk.sk_state == SMC_INIT)
596+
sock_put(&smc->sk); /* passive closing */
597+
return rc;
598+
}
582599
smc_copy_sock_settings_to_clc(smc);
583600
smc->connect_nonblock = 0;
584601
if (smc->sk.sk_state == SMC_INIT)
@@ -1384,11 +1401,12 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
13841401
{
13851402
/* RDMA setup failed, switch back to TCP */
13861403
smc_conn_abort(new_smc, local_first);
1387-
if (reason_code < 0) { /* error, no fallback possible */
1404+
if (reason_code < 0 ||
1405+
smc_switch_to_fallback(new_smc, reason_code)) {
1406+
/* error, no fallback possible */
13881407
smc_listen_out_err(new_smc);
13891408
return;
13901409
}
1391-
smc_switch_to_fallback(new_smc, reason_code);
13921410
if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) {
13931411
if (smc_clc_send_decline(new_smc, reason_code, version) < 0) {
13941412
smc_listen_out_err(new_smc);
@@ -1761,8 +1779,11 @@ static void smc_listen_work(struct work_struct *work)
17611779

17621780
/* check if peer is smc capable */
17631781
if (!tcp_sk(newclcsock->sk)->syn_smc) {
1764-
smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC);
1765-
smc_listen_out_connected(new_smc);
1782+
rc = smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC);
1783+
if (rc)
1784+
smc_listen_out_err(new_smc);
1785+
else
1786+
smc_listen_out_connected(new_smc);
17661787
return;
17671788
}
17681789

@@ -2048,7 +2069,9 @@ static int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
20482069

20492070
if (msg->msg_flags & MSG_FASTOPEN) {
20502071
if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
2051-
smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2072+
rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2073+
if (rc)
2074+
goto out;
20522075
} else {
20532076
rc = -EINVAL;
20542077
goto out;
@@ -2241,6 +2264,11 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
22412264
/* generic setsockopts reaching us here always apply to the
22422265
* CLC socket
22432266
*/
2267+
mutex_lock(&smc->clcsock_release_lock);
2268+
if (!smc->clcsock) {
2269+
mutex_unlock(&smc->clcsock_release_lock);
2270+
return -EBADF;
2271+
}
22442272
if (unlikely(!smc->clcsock->ops->setsockopt))
22452273
rc = -EOPNOTSUPP;
22462274
else
@@ -2250,6 +2278,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
22502278
sk->sk_err = smc->clcsock->sk->sk_err;
22512279
sk_error_report(sk);
22522280
}
2281+
mutex_unlock(&smc->clcsock_release_lock);
22532282

22542283
if (optlen < sizeof(int))
22552284
return -EINVAL;
@@ -2266,7 +2295,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
22662295
case TCP_FASTOPEN_NO_COOKIE:
22672296
/* option not supported by SMC */
22682297
if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
2269-
smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2298+
rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
22702299
} else {
22712300
rc = -EINVAL;
22722301
}
@@ -2309,13 +2338,23 @@ static int smc_getsockopt(struct socket *sock, int level, int optname,
23092338
char __user *optval, int __user *optlen)
23102339
{
23112340
struct smc_sock *smc;
2341+
int rc;
23122342

23132343
smc = smc_sk(sock->sk);
2344+
mutex_lock(&smc->clcsock_release_lock);
2345+
if (!smc->clcsock) {
2346+
mutex_unlock(&smc->clcsock_release_lock);
2347+
return -EBADF;
2348+
}
23142349
/* socket options apply to the CLC socket */
2315-
if (unlikely(!smc->clcsock->ops->getsockopt))
2350+
if (unlikely(!smc->clcsock->ops->getsockopt)) {
2351+
mutex_unlock(&smc->clcsock_release_lock);
23162352
return -EOPNOTSUPP;
2317-
return smc->clcsock->ops->getsockopt(smc->clcsock, level, optname,
2318-
optval, optlen);
2353+
}
2354+
rc = smc->clcsock->ops->getsockopt(smc->clcsock, level, optname,
2355+
optval, optlen);
2356+
mutex_unlock(&smc->clcsock_release_lock);
2357+
return rc;
23192358
}
23202359

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

0 commit comments

Comments
 (0)