Unloading a driver with asynchronous function callbacks

Hey,
(disclaimer: this is not code in production, just my curiosity…)
Let’s say I have the following code:

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegKey)
{
	//
	// Do initialization stuff..
	//

	return RegisterSomeEvent(&EventHandler);
}

NTSTATUS EventHandler(PSOME_EVENT SomeEvent)
{
	// 
	// Do some processing with the event...
	//
        return STATUS_SUCCESS;
}

NTSTATUS DriverUnload(PDRIVER_OBJECT DriverObject)
{
	//
	// Now I need to wait until all instances of the "EventHandler" function stops running..
	// How should I do it?
	//
}

The problem with this code is that the driver needs to wait until all the instances of the callback function stops running before unloading to prevent an access violation (running code from an unloaded driver…)

I know that the executive callback objects for example contains a rundown protection object, so the free function of the callback object waits until all the function calls finish. But in this case, “RegisterSomeEvent” does not guard the “EventHandler” call with a rundown protect, so I need to implement it myself somehow.

This is the first try:

EX_RUNDOWN_REF g_Protection;

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegKey)
{
	//
	// Do initialization stuff..
	//
	ExInitializeRundownProtection(&g_Protection);
	RegisterSomeEvent(&EventHandler);
}

NTSTATUS EventHandler(PSOME_EVENT SomeEvent)
{
	//
	//  Acquire rundown protection..
	//
	if (!ExAcquireRundownProtection(&g_Protection))
	{
		//
		// terminating, goodbye
		//
		return STATUS_UNSUCCESSFUL;
	}

	// 
	// Do some processing with the event...
	//
	NTSTATUS Status = Process(SomeEvent);

	//
	// Now release EX_RUNDOWN_REF
	//
	ExReleaseRundownProtection(&g_Protection);

	//
	// This code is not protected!
	// ExReleaseRundownProtection still returns to my code!
	//
	return Status;
}

NTSTATUS DriverUnload(PDRIVER_OBJECT DriverObject)
{
	UnregisterSomeEvnet(&EventHandler);

	//
	// Now I need to wait until all instances of the "EventHandler" function stops running..
	//
	ExWaitForRundownProtectionRelease(&g_Protection);

	return STATUS_SUCCESS;
}

This code still has a problem: After calling ExReleaseRundownProtection() my code still runs. I thought of 2 ways to solve this:

  1. The safest way (I guess) is to perform a “tail jump” outside of the EventHandler function. To do this safely, I have to write inline assembly. (I cannot count on the compiler to make a tail jump for sure…)
  2. After calling ExWaitForRundownProtectionRelease, I can simply use “KeDelayExecutionThread” inside the DriverUnload routine and wait for an arbitrary time-frame and hope the “return X” statements will run until then.

Finally, my questions are:

  1. How should I solve this problem?
  2. Have you encountered a similar problem before?
  3. How is this implemented with actual IRP handlers? Let’s say I have a device object that receives many IO requests, If I call IoDeleteDevice, will it block until all IRP handlers finish running?

Thanks :smiley: