@@ -279,7 +279,7 @@ VOID CleanupNotification()
279279 // Cancel any pending IO
280280 if (g_notify .handle ) {
281281 while (g_notify .in_progress ) {
282- CancelIo (g_notify .handle );
282+ CancelIoEx (g_notify .handle , NULL );
283283 SleepConditionVariableCS (& g_notify .cv , & g_notify .lock , INFINITE );
284284 }
285285 CloseHandle (g_notify .handle );
@@ -310,14 +310,17 @@ UINT32 WaitForNotification(UINT32 event)
310310 UINT32 ievent = 0 ;
311311 NotificationRsp_t notify_response = {0 };
312312 NotificationReq_t notify_request = {0 };
313-
314- // Make sure Initialization has been done
315- if (g_notify .handle == INVALID_HANDLE_VALUE ) {
316- return 0 ;
317- }
313+ BOOL aborted = FALSE;
318314
319315 // Loop until we get event we are looking for
320316 for (;;) {
317+ // Make sure Initialization has been done
318+ // This is checked every iteration because CleanupNotification may have been called which
319+ // destroys the critical section, and attempting to enter it below is undefined behavior
320+ if (g_notify .handle == INVALID_HANDLE_VALUE ) {
321+ return 0 ;
322+ }
323+
321324 // There could be many calls into this function, only first call calls into KMDF driver
322325 // Subsequent calls just wait for the event to be set by the KMDF driver
323326 EnterCriticalSection (& g_notify .lock );
@@ -339,19 +342,33 @@ UINT32 WaitForNotification(UINT32 event)
339342 ) == TRUE )
340343 {
341344 g_notify .event = notify_response .lastevent ;
345+ // Tricky race condition where Cleanup cancels the IO call and we beat it back to the top
346+ // where we set in_progress to true again, then Cleanup calls cancel again but we haven't
347+ // entered IoControl call yet, but then we get there and Cleanup is sleeping but now we
348+ // are stuck in IoControl and can't return to Wake it again resulting in deadlock.
349+ // So we explicitly check if we returned due to being cancelled and bail out of the loop to prevent this.
350+ } else if (GetLastError () == ERROR_OPERATION_ABORTED ) {
351+ aborted = TRUE;
342352 } else {
343353 g_notify .event = 0 ;
344354 }
345355
356+ // Enter critical section here to ensure wake is caught by Cleanup
357+ EnterCriticalSection (& g_notify .lock );
346358 g_notify .in_progress = FALSE;
347359 WakeAllConditionVariable (& g_notify .cv );
348360 } else {
349361 // Wait for notification to be set
350- LeaveCriticalSection (& g_notify .lock );
351- SleepConditionVariableCS (& g_notify .cv , & g_notify .lock , INFINITE );
362+ // Loop for spurious wakeups
363+ while (g_notify .in_progress ) {
364+ SleepConditionVariableCS (& g_notify .cv , & g_notify .lock , INFINITE );
365+ }
352366 }
353367
354- if (event == 0 || g_notify .event == event ) {
368+ // Regardless of branch we are always in a critical section at this point so leave it
369+ LeaveCriticalSection (& g_notify .lock );
370+
371+ if (aborted || event == 0 || g_notify .event == event ) {
355372 ievent = g_notify .event ;
356373 break ;
357374 }
0 commit comments