Skip to content

Commit

Permalink
Fix CASEServer cleanup to not nuke unrelated CASE handshakes.
Browse files Browse the repository at this point in the history
We could run into a problem when an invalid Sigma1 (e.g. not matching any
fabric) was received, because we would do the following:

1) Call CASEServer::OnSessionEstablishmentError from inside
   CASESession::OnMessageReceived, which would queue a task to blow away the
   CASEServer state and start listening for new CASE handshakes
   (PrepareForSessionEstablishment).

2) Return an error from CASESession::OnMessageReceived, which would cause us to
   synchronously do call PrepareForSessionEstablishment.

If we now got a new Sigma1 before the async task ran (e.g. if it was already
waiting in the queue), we could respond to the Sigma1 with Sigma2Resume, then
the async task would run and blow away our state.

We would end up in a situation where the other side then sends a StatusResponse
and thinks CASE is established, but on our end we don't recognize the session ID
for the session they think has been established.

The fix is to restrict the async behavior to the one case it's actually needed
(OnSessionReleased), for both CASE and PASE, to synchronously call
PrepareForSessionEstablishment from OnSessionEstablishmentError, and to remove
the now-redundant PrepareForSessionEstablishment call on error return from
CASESession::OnMessageReceived.
  • Loading branch information
bzbarsky-apple committed Nov 16, 2022
1 parent bbf3111 commit f4ac24c
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 19 deletions.
18 changes: 3 additions & 15 deletions src/protocols/secure_channel/CASEServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,8 @@ CHIP_ERROR CASEServer::OnMessageReceived(Messaging::ExchangeContext * ec, const
SuccessOrExit(err);

exit:
if (err != CHIP_NO_ERROR)
{
PrepareForSessionEstablishment();
}

// CASESession::OnMessageReceived guarantees that it will call
// OnSessionEstablishmentError if it returns error, so nothing else to do here.
return err;
}

Expand Down Expand Up @@ -157,16 +154,7 @@ void CASEServer::OnSessionEstablishmentError(CHIP_ERROR err)
{
ChipLogError(Inet, "CASE Session establishment failed: %" CHIP_ERROR_FORMAT, err.Format());

//
// We're not allowed to call methods that will eventually result in calling SessionManager::AllocateSecureSession
// from a SessionDelegate::OnSessionReleased callback. Schedule the preparation as an async work item.
//
mSessionManager->SystemLayer()->ScheduleWork(
[](auto * systemLayer, auto * appState) -> void {
CASEServer * _this = static_cast<CASEServer *>(appState);
_this->PrepareForSessionEstablishment();
},
this);
PrepareForSessionEstablishment();
}

void CASEServer::OnSessionEstablished(const SessionHandle & session)
Expand Down
4 changes: 2 additions & 2 deletions src/protocols/secure_channel/CASESession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ CASESession::~CASESession()

void CASESession::OnSessionReleased()
{
// Call into our super-class before we clear our state.
PairingSession::OnSessionReleased();
Clear();
// Do this last in case the delegate frees us.
NotifySessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED);
}

void CASESession::Clear()
Expand Down
4 changes: 2 additions & 2 deletions src/protocols/secure_channel/PASESession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ PASESession::~PASESession()

void PASESession::OnSessionReleased()
{
// Call into our super-class before we clear our state.
PairingSession::OnSessionReleased();
Clear();
// Do this last in case the delegate frees us.
NotifySessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED);
}

void PASESession::Finish()
Expand Down
19 changes: 19 additions & 0 deletions src/protocols/secure_channel/PairingSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,23 @@ void PairingSession::NotifySessionEstablishmentError(CHIP_ERROR error)
delegate->OnSessionEstablishmentError(error);
}

void PairingSession::OnSessionReleased()
{
// Send the error notification async, because our delegate is likely to want
// to create a new session to listen for new connection attempts, and doing
// that under an OnSessionReleased notification is not safe.
if (!mSessionManager)
{
return;
}

mSessionManager->SystemLayer()->ScheduleWork(
[](auto * systemLayer, auto * appState) -> void {
ChipLogError(Inet, "ASYNC CASE Session establishment failed");
auto * _this = static_cast<PairingSession *>(appState);
_this->NotifySessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED);
},
this);
}

} // namespace chip
1 change: 1 addition & 0 deletions src/protocols/secure_channel/PairingSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class DLL_EXPORT PairingSession : public SessionDelegate

// Implement SessionDelegate
NewSessionHandlingPolicy GetNewSessionHandlingPolicy() override { return NewSessionHandlingPolicy::kStayAtOldSession; }
void OnSessionReleased() override;

Optional<uint16_t> GetLocalSessionId() const
{
Expand Down

0 comments on commit f4ac24c

Please sign in to comment.