Skip to content

Commit b081572

Browse files
Fix OperationalSessionSetup notifying success with inactive sessions (#33822)
When a success callback marks the session defunct for some reason, other succcess callbacks should not be called. Implement a GroupedCallbackList to make this logic possible. This specific solution is based on the assumption that we don't want to change the OperationalSessionSetup API which takes success and two variants of failure callbacks as separate, client-provided Callback objects, with all callbacks being optional to provide. We also don't want to introduce additional dynamic allocation within OperationalSessionSetup e.g. to allocate a struct holding the related callbacks. The GroupedCallbackList class makes use of the existing prev/next pointers within the client-allocated Callback objects to capture the grouping relationship between them. Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent 0ae8a02 commit b081572

6 files changed

+522
-84
lines changed

src/app/OperationalSessionSetup.cpp

+43-77
Original file line numberDiff line numberDiff line change
@@ -325,35 +325,14 @@ void OperationalSessionSetup::EnqueueConnectionCallbacks(Callback::Callback<OnDe
325325
Callback::Callback<OnDeviceConnectionFailure> * onFailure,
326326
Callback::Callback<OnSetupFailure> * onSetupFailure)
327327
{
328-
if (onConnection != nullptr)
329-
{
330-
mConnectionSuccess.Enqueue(onConnection->Cancel());
331-
}
332-
333-
if (onFailure != nullptr)
334-
{
335-
mConnectionFailure.Enqueue(onFailure->Cancel());
336-
}
337-
338-
if (onSetupFailure != nullptr)
339-
{
340-
mSetupFailure.Enqueue(onSetupFailure->Cancel());
341-
}
328+
mCallbacks.Enqueue(onConnection, onFailure, onSetupFailure);
342329
}
343330

344331
void OperationalSessionSetup::DequeueConnectionCallbacks(CHIP_ERROR error, SessionEstablishmentStage stage,
345332
ReleaseBehavior releaseBehavior)
346333
{
347-
Cancelable failureReady, setupFailureReady, successReady;
348-
349-
//
350-
// Dequeue both failure and success callback lists into temporary stack args before invoking either of them.
351-
// We do this since we may not have a valid 'this' pointer anymore upon invoking any of those callbacks
352-
// since the callee may destroy this object as part of that callback.
353-
//
354-
mConnectionFailure.DequeueAll(failureReady);
355-
mSetupFailure.DequeueAll(setupFailureReady);
356-
mConnectionSuccess.DequeueAll(successReady);
334+
// We expect that we only have callbacks if we are not performing just address update.
335+
VerifyOrDie(!mPerformingAddressUpdate || mCallbacks.IsEmpty());
357336

358337
#if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
359338
// Clear out mConnectionRetry, so that those cancelables are not holding
@@ -365,7 +344,8 @@ void OperationalSessionSetup::DequeueConnectionCallbacks(CHIP_ERROR error, Sessi
365344
#endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
366345

367346
// Gather up state we will need for our notifications.
368-
bool performingAddressUpdate = mPerformingAddressUpdate;
347+
SuccessFailureCallbackList readyCallbacks;
348+
readyCallbacks.EnqueueTakeAll(mCallbacks);
369349
auto * exchangeMgr = mInitParams.exchangeMgr;
370350
Optional<SessionHandle> optionalSessionHandle = mSecureSession.Get();
371351
ScopedNodeId peerId = mPeerId;
@@ -383,71 +363,57 @@ void OperationalSessionSetup::DequeueConnectionCallbacks(CHIP_ERROR error, Sessi
383363
}
384364

385365
// DO NOT touch any members of this object after this point. It's dead.
386-
387-
NotifyConnectionCallbacks(failureReady, setupFailureReady, successReady, error, stage, peerId, performingAddressUpdate,
388-
exchangeMgr, optionalSessionHandle, requestedBusyDelay);
366+
NotifyConnectionCallbacks(readyCallbacks, error, stage, peerId, exchangeMgr, optionalSessionHandle, requestedBusyDelay);
389367
}
390368

391-
void OperationalSessionSetup::NotifyConnectionCallbacks(Cancelable & failureReady, Cancelable & setupFailureReady,
392-
Cancelable & successReady, CHIP_ERROR error,
369+
void OperationalSessionSetup::NotifyConnectionCallbacks(SuccessFailureCallbackList & ready, CHIP_ERROR error,
393370
SessionEstablishmentStage stage, const ScopedNodeId & peerId,
394-
bool performingAddressUpdate, Messaging::ExchangeManager * exchangeMgr,
371+
Messaging::ExchangeManager * exchangeMgr,
395372
const Optional<SessionHandle> & optionalSessionHandle,
396373
System::Clock::Milliseconds16 requestedBusyDelay)
397374
{
398-
//
399-
// If we encountered no error, go ahead and call all success callbacks. Otherwise,
400-
// call the failure callbacks.
401-
//
402-
while (failureReady.mNext != &failureReady)
375+
Callback::Callback<OnDeviceConnected> * onConnected;
376+
Callback::Callback<OnDeviceConnectionFailure> * onConnectionFailure;
377+
Callback::Callback<OnSetupFailure> * onSetupFailure;
378+
while (ready.Take(onConnected, onConnectionFailure, onSetupFailure))
403379
{
404-
// We expect that we only have callbacks if we are not performing just address update.
405-
VerifyOrDie(!performingAddressUpdate);
406-
Callback::Callback<OnDeviceConnectionFailure> * cb =
407-
Callback::Callback<OnDeviceConnectionFailure>::FromCancelable(failureReady.mNext);
408-
409-
cb->Cancel();
410-
411-
if (error != CHIP_NO_ERROR)
380+
if (error == CHIP_NO_ERROR)
412381
{
413-
cb->mCall(cb->mContext, peerId, error);
382+
VerifyOrDie(exchangeMgr);
383+
VerifyOrDie(optionalSessionHandle.Value()->AsSecureSession()->IsActiveSession());
384+
if (onConnected != nullptr)
385+
{
386+
onConnected->mCall(onConnected->mContext, *exchangeMgr, optionalSessionHandle.Value());
387+
388+
// That sucessful call might have made the session inactive. If it did, then we should
389+
// not call any more success callbacks, since we do not in fact have an active session
390+
// for them, and if they try to put the session in a holder that will fail, and then
391+
// trying to use the holder as if it has a session will crash.
392+
if (!optionalSessionHandle.Value()->AsSecureSession()->IsActiveSession())
393+
{
394+
ChipLogError(Discovery, "Success callback for connection to " ChipLogFormatScopedNodeId " tore down session",
395+
ChipLogValueScopedNodeId(peerId));
396+
error = CHIP_ERROR_CONNECTION_ABORTED;
397+
}
398+
}
414399
}
415-
}
416-
417-
while (setupFailureReady.mNext != &setupFailureReady)
418-
{
419-
// We expect that we only have callbacks if we are not performing just address update.
420-
VerifyOrDie(!performingAddressUpdate);
421-
Callback::Callback<OnSetupFailure> * cb = Callback::Callback<OnSetupFailure>::FromCancelable(setupFailureReady.mNext);
422-
423-
cb->Cancel();
424-
425-
if (error != CHIP_NO_ERROR)
400+
else // error
426401
{
427-
// Initialize the ConnnectionFailureInfo object
428-
ConnnectionFailureInfo failureInfo(peerId, error, stage);
429-
#if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
430-
if (error == CHIP_ERROR_BUSY)
402+
if (onConnectionFailure != nullptr)
431403
{
432-
failureInfo.requestedBusyDelay.Emplace(requestedBusyDelay);
404+
onConnectionFailure->mCall(onConnectionFailure->mContext, peerId, error);
433405
}
406+
if (onSetupFailure != nullptr)
407+
{
408+
ConnnectionFailureInfo failureInfo(peerId, error, stage);
409+
#if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
410+
if (error == CHIP_ERROR_BUSY)
411+
{
412+
failureInfo.requestedBusyDelay.Emplace(requestedBusyDelay);
413+
}
434414
#endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
435-
cb->mCall(cb->mContext, failureInfo);
436-
}
437-
}
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());
415+
onSetupFailure->mCall(onSetupFailure->mContext, failureInfo);
416+
}
451417
}
452418
}
453419
}

src/app/OperationalSessionSetup.h

+5-7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <app/util/basic-types.h>
3434
#include <credentials/GroupDataProvider.h>
3535
#include <lib/address_resolve/AddressResolve.h>
36+
#include <lib/core/GroupedCallbackList.h>
3637
#include <messaging/ExchangeContext.h>
3738
#include <messaging/ExchangeDelegate.h>
3839
#include <messaging/ExchangeMgr.h>
@@ -309,9 +310,8 @@ class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate,
309310

310311
SessionHolder mSecureSession;
311312

312-
Callback::CallbackDeque mConnectionSuccess;
313-
Callback::CallbackDeque mConnectionFailure;
314-
Callback::CallbackDeque mSetupFailure;
313+
typedef Callback::GroupedCallbackList<OnDeviceConnected, OnDeviceConnectionFailure, OnSetupFailure> SuccessFailureCallbackList;
314+
SuccessFailureCallbackList mCallbacks;
315315

316316
OperationalSessionReleaseDelegate * mReleaseDelegate;
317317

@@ -402,10 +402,8 @@ class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate,
402402
* notifications. This happens after the object has been released, if it's
403403
* being released.
404404
*/
405-
static void NotifyConnectionCallbacks(Callback::Cancelable & failureReady, Callback::Cancelable & setupFailureReady,
406-
Callback::Cancelable & successReady, CHIP_ERROR error, SessionEstablishmentStage stage,
407-
const ScopedNodeId & peerId, bool performingAddressUpdate,
408-
Messaging::ExchangeManager * exchangeMgr,
405+
static void NotifyConnectionCallbacks(SuccessFailureCallbackList & ready, CHIP_ERROR error, SessionEstablishmentStage stage,
406+
const ScopedNodeId & peerId, Messaging::ExchangeManager * exchangeMgr,
409407
const Optional<SessionHandle> & optionalSessionHandle,
410408
// requestedBusyDelay will be 0 if not
411409
// CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP,

src/lib/core/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ static_library("core") {
157157
"CHIPKeyIds.h",
158158
"CHIPPersistentStorageDelegate.h",
159159
"ClusterEnums.h",
160+
"GroupedCallbackList.h",
160161
"OTAImageHeader.cpp",
161162
"OTAImageHeader.h",
162163
"PeerId.h",

0 commit comments

Comments
 (0)