Skip to content

Commit 60034d3

Browse files
committed
rxrpc: Fix potential deadlock
There is a potential deadlock in rxrpc_peer_keepalive_dispatch() whereby rxrpc_put_peer() is called with the peer_hash_lock held, but if it reduces the peer's refcount to 0, rxrpc_put_peer() calls __rxrpc_put_peer() - which the tries to take the already held lock. Fix this by providing a version of rxrpc_put_peer() that can be called in situations where the lock is already held. The bug may produce the following lockdep report: ============================================ WARNING: possible recursive locking detected 5.2.0-next-20190718 hardkernel#41 Not tainted -------------------------------------------- kworker/0:3/21678 is trying to acquire lock: 00000000aa5eecdf (&(&rxnet->peer_hash_lock)->rlock){+.-.}, at: spin_lock_bh /./include/linux/spinlock.h:343 [inline] 00000000aa5eecdf (&(&rxnet->peer_hash_lock)->rlock){+.-.}, at: __rxrpc_put_peer /net/rxrpc/peer_object.c:415 [inline] 00000000aa5eecdf (&(&rxnet->peer_hash_lock)->rlock){+.-.}, at: rxrpc_put_peer+0x2d3/0x6a0 /net/rxrpc/peer_object.c:435 but task is already holding lock: 00000000aa5eecdf (&(&rxnet->peer_hash_lock)->rlock){+.-.}, at: spin_lock_bh /./include/linux/spinlock.h:343 [inline] 00000000aa5eecdf (&(&rxnet->peer_hash_lock)->rlock){+.-.}, at: rxrpc_peer_keepalive_dispatch /net/rxrpc/peer_event.c:378 [inline] 00000000aa5eecdf (&(&rxnet->peer_hash_lock)->rlock){+.-.}, at: rxrpc_peer_keepalive_worker+0x6b3/0xd02 /net/rxrpc/peer_event.c:430 Fixes: 330bdcf ("rxrpc: Fix the keepalive generator [ver #2]") Reported-by: [email protected] Signed-off-by: David Howells <[email protected]> Reviewed-by: Marc Dionne <[email protected]> Reviewed-by: Jeffrey Altman <[email protected]>
1 parent a20961c commit 60034d3

File tree

3 files changed

+20
-1
lines changed

3 files changed

+20
-1
lines changed

net/rxrpc/ar-internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,7 @@ void rxrpc_destroy_all_peers(struct rxrpc_net *);
10611061
struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *);
10621062
struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *);
10631063
void rxrpc_put_peer(struct rxrpc_peer *);
1064+
void rxrpc_put_peer_locked(struct rxrpc_peer *);
10641065

10651066
/*
10661067
* proc.c

net/rxrpc/peer_event.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet,
378378
spin_lock_bh(&rxnet->peer_hash_lock);
379379
list_add_tail(&peer->keepalive_link,
380380
&rxnet->peer_keepalive[slot & mask]);
381-
rxrpc_put_peer(peer);
381+
rxrpc_put_peer_locked(peer);
382382
}
383383

384384
spin_unlock_bh(&rxnet->peer_hash_lock);

net/rxrpc/peer_object.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,24 @@ void rxrpc_put_peer(struct rxrpc_peer *peer)
436436
}
437437
}
438438

439+
/*
440+
* Drop a ref on a peer record where the caller already holds the
441+
* peer_hash_lock.
442+
*/
443+
void rxrpc_put_peer_locked(struct rxrpc_peer *peer)
444+
{
445+
const void *here = __builtin_return_address(0);
446+
int n;
447+
448+
n = atomic_dec_return(&peer->usage);
449+
trace_rxrpc_peer(peer, rxrpc_peer_put, n, here);
450+
if (n == 0) {
451+
hash_del_rcu(&peer->hash_link);
452+
list_del_init(&peer->keepalive_link);
453+
kfree_rcu(peer, rcu);
454+
}
455+
}
456+
439457
/*
440458
* Make sure all peer records have been discarded.
441459
*/

0 commit comments

Comments
 (0)