diff --git a/src/core/listener.c b/src/core/listener.c index 97ba781523..15ec8a9c5a 100644 --- a/src/core/listener.c +++ b/src/core/listener.c @@ -14,12 +14,6 @@ #include "listener.c.clog.h" #endif -_IRQL_requires_max_(PASSIVE_LEVEL) -void -QuicListenerStopAsync( - _In_ QUIC_LISTENER* Listener - ); - BOOLEAN QuicListenerIsOnWorker( _In_ QUIC_LISTENER* Listener diff --git a/src/core/listener.h b/src/core/listener.h index f87a526608..6b4f7a1ac5 100644 --- a/src/core/listener.h +++ b/src/core/listener.h @@ -302,3 +302,12 @@ BOOLEAN QuicListenerDrainOperations( _In_ QUIC_LISTENER* Listener ); + +// +// Stops the listener asynchronously. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +void +QuicListenerStopAsync( + _In_ QUIC_LISTENER* Listener + ); diff --git a/src/core/registration.c b/src/core/registration.c index 4142e0b396..16095f4cd9 100644 --- a/src/core/registration.c +++ b/src/core/registration.c @@ -395,14 +395,36 @@ MsQuicRegistrationShutdown( Entry = Entry->Flink; } - CxPlatDispatchLockRelease(&Registration->ConnectionLock); + // + // Prevent close/shutdown races: once we drop the registration lock we must not + // depend on the shared listener list or listener lifetime. We snapshot listeners + // under lock and take a non-zero ref so only still-alive listeners are handled + // afterward. + // + CXPLAT_LIST_ENTRY PendingListeners; + CxPlatListInitializeHead(&PendingListeners); - Entry = Registration->Listeners.Flink; - while (Entry != &Registration->Listeners) { + while (!CxPlatListIsEmpty(&Registration->Listeners)) { + Entry = CxPlatListRemoveHead(&Registration->Listeners); QUIC_LISTENER* Listener = CXPLAT_CONTAINING_RECORD(Entry, QUIC_LISTENER, RegistrationLink); - Entry = Entry->Flink; - MsQuicListenerStop((HQUIC)Listener); + + if (CxPlatRefIncrementNonZero(&Listener->RefCount, 1)) { + CxPlatListInsertTail(&PendingListeners, &Listener->RegistrationLink); + } + } + + CxPlatDispatchLockRelease(&Registration->ConnectionLock); + + while (!CxPlatListIsEmpty(&PendingListeners)) { + QUIC_LISTENER* Listener = + CXPLAT_CONTAINING_RECORD( + CxPlatListRemoveHead(&PendingListeners), + QUIC_LISTENER, + RegistrationLink); + + QuicListenerStopAsync(Listener); + QuicListenerRelease(Listener); } }