| From 7f1b2efef62a42551d1bf71beae8e46e0c821cf3 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 13 Apr 2022 11:16:25 +0100 |
| Subject: rxrpc: Restore removed timer deletion |
| |
| From: David Howells <dhowells@redhat.com> |
| |
| [ Upstream commit ee3b0826b4764f6c13ad6db67495c5a1c38e9025 ] |
| |
| A recent patch[1] from Eric Dumazet flipped the order in which the |
| keepalive timer and the keepalive worker were cancelled in order to fix a |
| syzbot reported issue[2]. Unfortunately, this enables the mirror image bug |
| whereby the timer races with rxrpc_exit_net(), restarting the worker after |
| it has been cancelled: |
| |
| CPU 1 CPU 2 |
| =============== ===================== |
| if (rxnet->live) |
| <INTERRUPT> |
| rxnet->live = false; |
| cancel_work_sync(&rxnet->peer_keepalive_work); |
| rxrpc_queue_work(&rxnet->peer_keepalive_work); |
| del_timer_sync(&rxnet->peer_keepalive_timer); |
| |
| Fix this by restoring the removed del_timer_sync() so that we try to remove |
| the timer twice. If the timer runs again, it should see ->live == false |
| and not restart the worker. |
| |
| Fixes: 1946014ca3b1 ("rxrpc: fix a race in rxrpc_exit_net()") |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| cc: Eric Dumazet <edumazet@google.com> |
| cc: Marc Dionne <marc.dionne@auristor.com> |
| cc: linux-afs@lists.infradead.org |
| Link: https://lore.kernel.org/r/20220404183439.3537837-1-eric.dumazet@gmail.com/ [1] |
| Link: https://syzkaller.appspot.com/bug?extid=724378c4bb58f703b09a [2] |
| Reviewed-by: Eric Dumazet <edumazet@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/rxrpc/net_ns.c | 2 ++ |
| 1 file changed, 2 insertions(+) |
| |
| diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c |
| index 9a76b74af37b..91a503871116 100644 |
| --- a/net/rxrpc/net_ns.c |
| +++ b/net/rxrpc/net_ns.c |
| @@ -116,7 +116,9 @@ static __net_exit void rxrpc_exit_net(struct net *net) |
| struct rxrpc_net *rxnet = rxrpc_net(net); |
| |
| rxnet->live = false; |
| + del_timer_sync(&rxnet->peer_keepalive_timer); |
| cancel_work_sync(&rxnet->peer_keepalive_work); |
| + /* Remove the timer again as the worker may have restarted it. */ |
| del_timer_sync(&rxnet->peer_keepalive_timer); |
| rxrpc_destroy_all_calls(rxnet); |
| rxrpc_destroy_all_connections(rxnet); |
| -- |
| 2.35.1 |
| |