Debugging a driver today found some issues that have not been considered at ordinary times. When _ Try and _ finally are used in the driver, what happens when the _ Try block directly uses return!
I used the example in wdk as my test code. Through Reverse detection, I found that the return in the _ Try block will not be executed into _ finally, but will return immediately. Therefore, using return directly in the _ Try block is very dangerous. We strongly recommend that you do not do this! I suffered a lot today.
The following is the code I used for testing. The following code can be found:
NTSTATUS DriverEntry(__in PDRIVER_OBJECT DriverObject, __in PUNICODE_STRING RegistryPath){ PDEVICE_OBJECT deviceObject; PDEVICE_EXTENSION deviceExtension; UNICODE_STRING ntDeviceName; UNICODE_STRING symbolicLinkName; NTSTATUS status; UNREFERENCED_PARAMETER(RegistryPath); DebugPrint(("==>DriverEntry\n")); __try { // // Create the device object // RtlInitUnicodeString(&ntDeviceName, NTDEVICE_NAME_STRING); status = IoCreateDevice(DriverObject, // DriverObject sizeof(DEVICE_EXTENSION), // DeviceExtensionSize &ntDeviceName, // DeviceName FILE_DEVICE_UNKNOWN, // DeviceType FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics FALSE, // Not Exclusive &deviceObject // DeviceObject ); if (!NT_SUCCESS(status)) { DebugPrint(("\IoCreateDevice returned 0x%x\n", status)); return status; } else { // // Set up dispatch entry points for the driver. // DriverObject->MajorFunction[IRP_MJ_CREATE] = EventCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = EventCreateClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = EventCleanup; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = EventDispatchIoControl; DriverObject->DriverUnload = EventUnload; // // Create a symbolic link for userapp to interact with the driver. // RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_NAME_STRING); status = IoCreateSymbolicLink(&symbolicLinkName, &ntDeviceName); // // Initialize the device extension. // deviceExtension = deviceObject->DeviceExtension; InitializeListHead(&deviceExtension->EventQueueHead); KeInitializeSpinLock(&deviceExtension->QueueLock); deviceExtension->Self = deviceObject; // // Establish user-buffer access method. // deviceObject->Flags |= DO_BUFFERED_IO; DebugPrint(("<==DriverEntry\n")); ASSERT(NT_SUCCESS(status)); } } __finally { if (!NT_SUCCESS(status)) { IoDeleteDevice(deviceObject); DebugPrint(("\tIoCreateSymbolicLink returned 0x%x\n", status)); } } return status;}
The _ Try block in the above function will return directly. Let's use IDA to see what the Assembly Code looks like.
Let me split it into two versions: checked and free.
First, let's look at the Assembly of the checked version:
...............
From the above we can see that the return code is directly returned, and it is not executed into the code in _ finally. The return code is first executed!
Let's see what the free version of Assembly looks like:
When iocreatedevice fails, it returns directly and does not execute it in _ finally.
The real _ Finally block is placed at the end of execution, __try block will not execute any returned results into _ finally.
Therefore, the code in the _ Try block is not executed in both the checked and free versions, which is very dangerous.My conclusion is that it is best not to use exception handling in the driver. Write it down today and make a memo!