rxrpc: Make service call handling more robust
Make the following changes to improve the robustness of the code that sets
up a new service call:
(1) Cache the rxrpc_sock struct obtained in rxrpc_data_ready() to do a
service ID check and pass that along to rxrpc_new_incoming_call().
This means that I can remove the check from rxrpc_new_incoming_call()
without the need to worry about the socket attached to the local
endpoint getting replaced - which would invalidate the check.
(2) Cache the rxrpc_peer struct, thereby allowing the peer search to be
done once. The peer is passed to rxrpc_new_incoming_call(), thereby
saving the need to repeat the search.
This also reduces the possibility of rxrpc_publish_service_conn()
BUG()'ing due to the detection of a duplicate connection, despite the
initial search done by rxrpc_find_connection_rcu() having turned up
nothing.
This BUG() shouldn't ever get hit since rxrpc_data_ready() *should* be
non-reentrant and the result of the initial search should still hold
true, but it has proven possible to hit.
I *think* this may be due to __rxrpc_lookup_peer_rcu() cutting short
the iteration over the hash table if it finds a matching peer with a
zero usage count, but I don't know for sure since it's only ever been
hit once that I know of.
Another possibility is that a bug in rxrpc_data_ready() that checked
the wrong byte in the header for the RXRPC_CLIENT_INITIATED flag
might've let through a packet that caused a spurious and invalid call
to be set up. That is addressed in another patch.
(3) Fix __rxrpc_lookup_peer_rcu() to skip peer records that have a zero
usage count rather than stopping and returning not found, just in case
there's another peer record behind it in the bucket.
(4) Don't search the peer records in rxrpc_alloc_incoming_call(), but
rather either use the peer cached in (2) or, if one wasn't found,
preemptively install a new one.
Fixes: 8496af50eb
("rxrpc: Use RCU to access a peer's service connection tree")
Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
@@ -1128,7 +1128,8 @@ void rxrpc_data_ready(struct sock *udp_sk)
|
||||
struct rxrpc_call *call = NULL;
|
||||
struct rxrpc_skb_priv *sp;
|
||||
struct rxrpc_local *local = udp_sk->sk_user_data;
|
||||
struct rxrpc_sock *rx;
|
||||
struct rxrpc_peer *peer = NULL;
|
||||
struct rxrpc_sock *rx = NULL;
|
||||
struct sk_buff *skb;
|
||||
unsigned int channel;
|
||||
int ret, skew = 0;
|
||||
@@ -1250,7 +1251,7 @@ void rxrpc_data_ready(struct sock *udp_sk)
|
||||
}
|
||||
}
|
||||
|
||||
conn = rxrpc_find_connection_rcu(local, skb);
|
||||
conn = rxrpc_find_connection_rcu(local, skb, &peer);
|
||||
if (conn) {
|
||||
if (sp->hdr.securityIndex != conn->security_ix)
|
||||
goto wrong_security;
|
||||
@@ -1339,7 +1340,7 @@ void rxrpc_data_ready(struct sock *udp_sk)
|
||||
goto bad_message_unlock;
|
||||
if (sp->hdr.seq != 1)
|
||||
goto discard_unlock;
|
||||
call = rxrpc_new_incoming_call(local, conn, skb);
|
||||
call = rxrpc_new_incoming_call(local, rx, peer, conn, skb);
|
||||
if (!call) {
|
||||
rcu_read_unlock();
|
||||
goto reject_packet;
|
||||
|
Reference in New Issue
Block a user