Skip to content

Commit 3025216

Browse files
Fix OperationalSessionSetup notifying success with inactive sessions.
When a success callback marks the session defunct for some reason, the other succcess callbacks should probably not be called.
1 parent d84f13e commit 3025216

File tree

1 file changed

+74
-17
lines changed

1 file changed

+74
-17
lines changed

src/app/OperationalSessionSetup.cpp

+74-17
Original file line numberDiff line numberDiff line change
@@ -396,9 +396,81 @@ void OperationalSessionSetup::NotifyConnectionCallbacks(Cancelable & failureRead
396396
System::Clock::Milliseconds16 requestedBusyDelay)
397397
{
398398
//
399-
// If we encountered no error, go ahead and call all success callbacks. Otherwise,
400-
// call the failure callbacks.
399+
// Go ahead and call our callbacks. We want to walk through all the lists,
400+
// because we need to Cancel() each callback no matter what. Which
401+
// callbacks we then call depends on whether we have encountered an error.
401402
//
403+
while (successReady.mNext != &successReady)
404+
{
405+
// We expect that we only have callbacks if we are not performing just address update.
406+
VerifyOrDie(!performingAddressUpdate);
407+
Callback::Callback<OnDeviceConnected> * cb = Callback::Callback<OnDeviceConnected>::FromCancelable(successReady.mNext);
408+
409+
cb->Cancel();
410+
if (error == CHIP_NO_ERROR)
411+
{
412+
VerifyOrDie(exchangeMgr);
413+
// We know that we for sure have the SessionHandle in the successful
414+
// case, and the session is active.
415+
VerifyOrDie(optionalSessionHandle.Value()->AsSecureSession()->IsActiveSession());
416+
cb->mCall(cb->mContext, *exchangeMgr, optionalSessionHandle.Value());
417+
418+
// That call might have made the session inactive. If it did, then
419+
// we should not call any more success callbacks, since we do not in
420+
// fact have an active session for them, and if they try to put the
421+
// session in a holder that will fail, and then trying to use the
422+
// holder as if it has a session will crash. But we should also try
423+
// to make sure we don't call failure callbacks the correspond to
424+
// the success callbacks we already called, since their context
425+
// might be dead.
426+
//
427+
// What that means is that after every success callback we should
428+
// remove the corresponding failure callbacks from our lists.
429+
// Unfortunately, callers are allowed to pass null callbacks, so we
430+
// can't just remove the first one, and in any case we have multiple
431+
// kinds of error callbacks. But what we should probably do is just
432+
// remove the first callback that has the same context as the
433+
// callback we just called. And if there is more than one that
434+
// matches, clearly the API consumer was not expecting to delete
435+
// itself when called, so we're OK with only dropping the first one.
436+
//
437+
// This would all be simpler if we just had a single callback per
438+
// API consumer that communicated success-or-failure in a single
439+
// call...
440+
for (auto * failureCancelable = failureReady.mNext; failureCancelable != &failureReady;
441+
failureCancelable = failureCancelable->mNext)
442+
{
443+
auto * failureCallback = Callback::Callback<OnDeviceConnectionFailure>::FromCancelable(failureCancelable);
444+
if (failureCallback->mContext == cb->mContext)
445+
{
446+
failureCallback->Cancel();
447+
break;
448+
}
449+
}
450+
451+
for (auto * setupFailureCancelable = setupFailureReady.mNext; setupFailureCancelable != &setupFailureReady;
452+
setupFailureCancelable = setupFailureCancelable->mNext)
453+
{
454+
auto * setupFailureCallback = Callback::Callback<OnSetupFailure>::FromCancelable(setupFailureCancelable);
455+
if (setupFailureCallback->mContext == cb->mContext)
456+
{
457+
setupFailureCallback->Cancel();
458+
break;
459+
}
460+
}
461+
462+
if (!optionalSessionHandle.Value()->AsSecureSession()->IsActiveSession())
463+
{
464+
// Set error to a non-success value so that we stop calling
465+
// success callbacks. We will call the relevant remaining error
466+
// callbacks in the blocks below.
467+
ChipLogError(Discovery, "Success callback for connection to " ChipLogFormatScopedNodeId " tore down session",
468+
ChipLogValueScopedNodeId(peerId));
469+
error = CHIP_ERROR_CONNECTION_ABORTED;
470+
}
471+
}
472+
}
473+
402474
while (failureReady.mNext != &failureReady)
403475
{
404476
// We expect that we only have callbacks if we are not performing just address update.
@@ -435,21 +507,6 @@ void OperationalSessionSetup::NotifyConnectionCallbacks(Cancelable & failureRead
435507
cb->mCall(cb->mContext, failureInfo);
436508
}
437509
}
438-
439-
while (successReady.mNext != &successReady)
440-
{
441-
// We expect that we only have callbacks if we are not performing just address update.
442-
VerifyOrDie(!performingAddressUpdate);
443-
Callback::Callback<OnDeviceConnected> * cb = Callback::Callback<OnDeviceConnected>::FromCancelable(successReady.mNext);
444-
445-
cb->Cancel();
446-
if (error == CHIP_NO_ERROR)
447-
{
448-
VerifyOrDie(exchangeMgr);
449-
// We know that we for sure have the SessionHandle in the successful case.
450-
cb->mCall(cb->mContext, *exchangeMgr, optionalSessionHandle.Value());
451-
}
452-
}
453510
}
454511

455512
void OperationalSessionSetup::OnSessionEstablishmentError(CHIP_ERROR error, SessionEstablishmentStage stage)

0 commit comments

Comments
 (0)