| From 938a78f02e42da82d16141a9432bad97b1b74152 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 6 Feb 2020 13:55:01 +0000 |
| Subject: rxrpc: Fix service call disconnection |
| |
| From: David Howells <dhowells@redhat.com> |
| |
| [ Upstream commit b39a934ec72fa2b5a74123891f25273a38378b90 ] |
| |
| The recent patch that substituted a flag on an rxrpc_call for the |
| connection pointer being NULL as an indication that a call was disconnected |
| puts the set_bit in the wrong place for service calls. This is only a |
| problem if a call is implicitly terminated by a new call coming in on the |
| same connection channel instead of a terminating ACK packet. |
| |
| In such a case, rxrpc_input_implicit_end_call() calls |
| __rxrpc_disconnect_call(), which is now (incorrectly) setting the |
| disconnection bit, meaning that when rxrpc_release_call() is later called, |
| it doesn't call rxrpc_disconnect_call() and so the call isn't removed from |
| the peer's error distribution list and the list gets corrupted. |
| |
| KASAN finds the issue as an access after release on a call, but the |
| position at which it occurs is confusing as it appears to be related to a |
| different call (the call site is where the latter call is being removed |
| from the error distribution list and either the next or pprev pointer |
| points to a previously released call). |
| |
| Fix this by moving the setting of the flag from __rxrpc_disconnect_call() |
| to rxrpc_disconnect_call() in the same place that the connection pointer |
| was being cleared. |
| |
| Fixes: 5273a191dca6 ("rxrpc: Fix NULL pointer deref due to call->conn being cleared on disconnect") |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/rxrpc/conn_object.c | 3 +-- |
| 1 file changed, 1 insertion(+), 2 deletions(-) |
| |
| diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c |
| index a81e64be4a24f..c4c4450891e0f 100644 |
| --- a/net/rxrpc/conn_object.c |
| +++ b/net/rxrpc/conn_object.c |
| @@ -174,8 +174,6 @@ void __rxrpc_disconnect_call(struct rxrpc_connection *conn, |
| |
| _enter("%d,%x", conn->debug_id, call->cid); |
| |
| - set_bit(RXRPC_CALL_DISCONNECTED, &call->flags); |
| - |
| if (rcu_access_pointer(chan->call) == call) { |
| /* Save the result of the call so that we can repeat it if necessary |
| * through the channel, whilst disposing of the actual call record. |
| @@ -228,6 +226,7 @@ void rxrpc_disconnect_call(struct rxrpc_call *call) |
| __rxrpc_disconnect_call(conn, call); |
| spin_unlock(&conn->channel_lock); |
| |
| + set_bit(RXRPC_CALL_DISCONNECTED, &call->flags); |
| conn->idle_timestamp = jiffies; |
| } |
| |
| -- |
| 2.20.1 |
| |