Skip to content

Commit 0558226

Browse files
Wen Gukuba-moo
authored andcommitted
net/smc: Fix slab-out-of-bounds issue in fallback
syzbot reported a slab-out-of-bounds/use-after-free issue, which was caused by accessing an already freed smc sock in fallback-specific callback functions of clcsock. This patch fixes the issue by restoring fallback-specific callback functions to original ones and resetting clcsock sk_user_data to NULL before freeing smc sock. Meanwhile, this patch introduces sk_callback_lock to make the access and assignment to sk_user_data mutually exclusive. Reported-by: [email protected] Fixes: 341adee ("net/smc: Forward wakeup to smc socket waitqueue after fallback") Link: https://lore.kernel.org/r/[email protected]/ Signed-off-by: Wen Gu <[email protected]> Acked-by: Karsten Graul <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 97b9af7 commit 0558226

File tree

2 files changed

+59
-23
lines changed

2 files changed

+59
-23
lines changed

net/smc/af_smc.c

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,27 @@ struct proto smc_proto6 = {
243243
};
244244
EXPORT_SYMBOL_GPL(smc_proto6);
245245

246+
static void smc_fback_restore_callbacks(struct smc_sock *smc)
247+
{
248+
struct sock *clcsk = smc->clcsock->sk;
249+
250+
write_lock_bh(&clcsk->sk_callback_lock);
251+
clcsk->sk_user_data = NULL;
252+
253+
smc_clcsock_restore_cb(&clcsk->sk_state_change, &smc->clcsk_state_change);
254+
smc_clcsock_restore_cb(&clcsk->sk_data_ready, &smc->clcsk_data_ready);
255+
smc_clcsock_restore_cb(&clcsk->sk_write_space, &smc->clcsk_write_space);
256+
smc_clcsock_restore_cb(&clcsk->sk_error_report, &smc->clcsk_error_report);
257+
258+
write_unlock_bh(&clcsk->sk_callback_lock);
259+
}
260+
246261
static void smc_restore_fallback_changes(struct smc_sock *smc)
247262
{
248263
if (smc->clcsock->file) { /* non-accepted sockets have no file yet */
249264
smc->clcsock->file->private_data = smc->sk.sk_socket;
250265
smc->clcsock->file = NULL;
266+
smc_fback_restore_callbacks(smc);
251267
}
252268
}
253269

@@ -745,48 +761,57 @@ static void smc_fback_forward_wakeup(struct smc_sock *smc, struct sock *clcsk,
745761

746762
static void smc_fback_state_change(struct sock *clcsk)
747763
{
748-
struct smc_sock *smc =
749-
smc_clcsock_user_data(clcsk);
764+
struct smc_sock *smc;
750765

751-
if (!smc)
752-
return;
753-
smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_state_change);
766+
read_lock_bh(&clcsk->sk_callback_lock);
767+
smc = smc_clcsock_user_data(clcsk);
768+
if (smc)
769+
smc_fback_forward_wakeup(smc, clcsk,
770+
smc->clcsk_state_change);
771+
read_unlock_bh(&clcsk->sk_callback_lock);
754772
}
755773

756774
static void smc_fback_data_ready(struct sock *clcsk)
757775
{
758-
struct smc_sock *smc =
759-
smc_clcsock_user_data(clcsk);
776+
struct smc_sock *smc;
760777

761-
if (!smc)
762-
return;
763-
smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_data_ready);
778+
read_lock_bh(&clcsk->sk_callback_lock);
779+
smc = smc_clcsock_user_data(clcsk);
780+
if (smc)
781+
smc_fback_forward_wakeup(smc, clcsk,
782+
smc->clcsk_data_ready);
783+
read_unlock_bh(&clcsk->sk_callback_lock);
764784
}
765785

766786
static void smc_fback_write_space(struct sock *clcsk)
767787
{
768-
struct smc_sock *smc =
769-
smc_clcsock_user_data(clcsk);
788+
struct smc_sock *smc;
770789

771-
if (!smc)
772-
return;
773-
smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_write_space);
790+
read_lock_bh(&clcsk->sk_callback_lock);
791+
smc = smc_clcsock_user_data(clcsk);
792+
if (smc)
793+
smc_fback_forward_wakeup(smc, clcsk,
794+
smc->clcsk_write_space);
795+
read_unlock_bh(&clcsk->sk_callback_lock);
774796
}
775797

776798
static void smc_fback_error_report(struct sock *clcsk)
777799
{
778-
struct smc_sock *smc =
779-
smc_clcsock_user_data(clcsk);
800+
struct smc_sock *smc;
780801

781-
if (!smc)
782-
return;
783-
smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_error_report);
802+
read_lock_bh(&clcsk->sk_callback_lock);
803+
smc = smc_clcsock_user_data(clcsk);
804+
if (smc)
805+
smc_fback_forward_wakeup(smc, clcsk,
806+
smc->clcsk_error_report);
807+
read_unlock_bh(&clcsk->sk_callback_lock);
784808
}
785809

786810
static void smc_fback_replace_callbacks(struct smc_sock *smc)
787811
{
788812
struct sock *clcsk = smc->clcsock->sk;
789813

814+
write_lock_bh(&clcsk->sk_callback_lock);
790815
clcsk->sk_user_data = (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
791816

792817
smc_clcsock_replace_cb(&clcsk->sk_state_change, smc_fback_state_change,
@@ -797,6 +822,8 @@ static void smc_fback_replace_callbacks(struct smc_sock *smc)
797822
&smc->clcsk_write_space);
798823
smc_clcsock_replace_cb(&clcsk->sk_error_report, smc_fback_error_report,
799824
&smc->clcsk_error_report);
825+
826+
write_unlock_bh(&clcsk->sk_callback_lock);
800827
}
801828

802829
static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
@@ -2370,17 +2397,20 @@ static void smc_tcp_listen_work(struct work_struct *work)
23702397

23712398
static void smc_clcsock_data_ready(struct sock *listen_clcsock)
23722399
{
2373-
struct smc_sock *lsmc =
2374-
smc_clcsock_user_data(listen_clcsock);
2400+
struct smc_sock *lsmc;
23752401

2402+
read_lock_bh(&listen_clcsock->sk_callback_lock);
2403+
lsmc = smc_clcsock_user_data(listen_clcsock);
23762404
if (!lsmc)
2377-
return;
2405+
goto out;
23782406
lsmc->clcsk_data_ready(listen_clcsock);
23792407
if (lsmc->sk.sk_state == SMC_LISTEN) {
23802408
sock_hold(&lsmc->sk); /* sock_put in smc_tcp_listen_work() */
23812409
if (!queue_work(smc_tcp_ls_wq, &lsmc->tcp_listen_work))
23822410
sock_put(&lsmc->sk);
23832411
}
2412+
out:
2413+
read_unlock_bh(&listen_clcsock->sk_callback_lock);
23842414
}
23852415

23862416
static int smc_listen(struct socket *sock, int backlog)
@@ -2412,10 +2442,12 @@ static int smc_listen(struct socket *sock, int backlog)
24122442
/* save original sk_data_ready function and establish
24132443
* smc-specific sk_data_ready function
24142444
*/
2445+
write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
24152446
smc->clcsock->sk->sk_user_data =
24162447
(void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
24172448
smc_clcsock_replace_cb(&smc->clcsock->sk->sk_data_ready,
24182449
smc_clcsock_data_ready, &smc->clcsk_data_ready);
2450+
write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
24192451

24202452
/* save original ops */
24212453
smc->ori_af_ops = inet_csk(smc->clcsock->sk)->icsk_af_ops;
@@ -2430,9 +2462,11 @@ static int smc_listen(struct socket *sock, int backlog)
24302462

24312463
rc = kernel_listen(smc->clcsock, backlog);
24322464
if (rc) {
2465+
write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
24332466
smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
24342467
&smc->clcsk_data_ready);
24352468
smc->clcsock->sk->sk_user_data = NULL;
2469+
write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
24362470
goto out;
24372471
}
24382472
sk->sk_max_ack_backlog = backlog;

net/smc/smc_close.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,11 @@ int smc_close_active(struct smc_sock *smc)
214214
sk->sk_state = SMC_CLOSED;
215215
sk->sk_state_change(sk); /* wake up accept */
216216
if (smc->clcsock && smc->clcsock->sk) {
217+
write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
217218
smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
218219
&smc->clcsk_data_ready);
219220
smc->clcsock->sk->sk_user_data = NULL;
221+
write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
220222
rc = kernel_sock_shutdown(smc->clcsock, SHUT_RDWR);
221223
}
222224
smc_close_cleanup_listen(sk);

0 commit comments

Comments
 (0)