Hooking \Device\RFCOMM

The driver creates a device object with all MajorFunction entries handled and attaches it to the \Device\RFCOMM device of rfcomm.sys. When the driver is unloading, it detaches and deletes the created device. Sometimes, this causes a BSOD, which seems to occur when an IRP is passed to the device after it has been detached.

Has anyone encountered this issue or have any suggestions on how to handle it properly?

WinDbg fulldump: Windows 11 24H2

KERNEL_SECURITY_CHECK_FAILURE (139)
A kernel component has corrupted a critical data structure. The corruption
could potentially allow a malicious user to gain control of this machine.
Arguments:
Arg1: 000000000000000a, Indirect call guard check detected invalid control transfer.
Arg2: 0000000000000000, Address of the trap frame for the exception that caused the BugCheck
Arg3: 0000000000000000, Address of the exception record for the exception that caused the BugCheck
Arg4: fffff8002cb15610, Reserved

Debugging Details:

KEY_VALUES_STRING: 1

Key  : Analysis.CPU.mSec
Value: 5296

Key  : Analysis.DebugAnalysisManager
Value: Create

Key  : Analysis.Elapsed.mSec
Value: 5761

Key  : Analysis.Init.CPU.mSec
Value: 2858

Key  : Analysis.Init.Elapsed.mSec
Value: 498739

Key  : Analysis.Memory.CommitPeak.Mb
Value: 91

Key  : WER.OS.Branch
Value: ge_release

Key  : WER.OS.Timestamp
Value: 2024-03-31T14:35:00Z

Key  : WER.OS.Version
Value: 10.0.26100.1

FILE_IN_CAB: MEMORY.DMP

TAG_NOT_DEFINED_202b: *** Unknown TAG in analysis list 202b

DUMP_FILE_ATTRIBUTES: 0x21800

BUGCHECK_CODE: 139

BUGCHECK_P1: a

BUGCHECK_P2: 0

BUGCHECK_P3: 0

BUGCHECK_P4: fffff8002cb15610

TRAP_FRAME: 0000000000000000 -- (.trap 0x0)

EXCEPTION_RECORD: 0000000000000000 -- (.exr 0x0)
Cannot read Exception record @ 0000000000000000

BLACKBOXBSD: 1 (!blackboxbsd)

BLACKBOXNTFS: 1 (!blackboxntfs)

BLACKBOXPNP: 1 (!blackboxpnp)

BLACKBOXWINLOGON: 1

PROCESS_NAME: svchost.exe

STACK_TEXT:
fffff58261e4f4b8 fffff800820849fe : 0000000000000139 000000000000000a 0000000000000000 0000000000000000 : nt!KeBugCheckEx
fffff58261e4f4c0 fffff80081cbffde : ffffcc8302536010 fffff80081c8afee ffffcc82e5d4c5b0 ffffcc8302536010 : nt!guard_icall_bugcheck+0x1e
fffff58261e4f4f0 fffff800161008e0 : ffffcc82e5d4c5b0 fffff58261e4f710 ffffb48000000000 ffffcc8302536010 : nt!IofCallDriver+0xbe
fffff58261e4f530 fffff800160ac0bc : 0000000000000000 0000000000000000 ffffcc83025361b8 fffff80082234f9e : afd!AfdGetAddress+0x3d0
fffff58261e4f630 fffff80081cbffde : ffffcc82e51c3cf0 ffffcc8301c17190 ffffcc8301c17190 ffffcc8301c17190 : afd!AfdDispatchDeviceControl+0x6c
fffff58261e4f660 fffff80082282e38 : ffffcc8301c17190 fffff58261e4f710 ffffcc82e51c3cf0 0000000000000000 : nt!IofCallDriver+0xbe
fffff58261e4f6a0 fffff80082281cd0 : ffffcc83051d46e0 0000000000000001 0000000000000001 0000000000000001 : nt!IopSynchronousServiceTail+0x1c8
fffff58261e4f750 fffff8008228137e : 0000000000000001 00000000000002e4 0000000000000000 0000000000000000 : nt!IopXxxControlFile+0x940
fffff58261e4f9c0 fffff8008208ef58 : 0000000000000000 0000000000000000 fffff58200000102 ffffcc82f58cdbe0 : nt!NtDeviceIoControlFile+0x5e
fffff58261e4fa30 00007ffd9a67c0a4 : 0000000000000000 0000000000000000 0000000000000000 0000000000000000 : nt!KiSystemServiceCopyEnd+0x28
0000006ad64ff478 0000000000000000 : 0000000000000000 0000000000000000 0000000000000000 0000000000000000 : 0x00007ffd`9a67c0a4

SYMBOL_NAME: afd!AfdGetAddress+3d0

MODULE_NAME: afd

IMAGE_NAME: afd.sys

STACK_COMMAND: .cxr; .ecxr ; kb

BUCKET_ID_FUNC_OFFSET: 3d0

FAILURE_BUCKET_ID: 0x139_a_GUARD_ICALL_CHECK_FAILURE_afd!AfdGetAddress

OS_VERSION: 10.0.26100.1

BUILDLAB_STR: ge_release

OSPLATFORM_TYPE: x64

OSNAME: Windows 10

FAILURE_ID_HASH: {f93f0b1f-1a94-f5ba-89d5-d9af884c122d}

Followup: MachineOwner

5: kd> !irp ffffcc83`02536010
Irp is active with 4 stacks 3 is current (= 0xffffcc8302536170)
Mdl=ffffcc82ff86d340: No System Buffer: Thread ffffcc83043230c0: Irp stack trace.
cmd flg cl Device File Completion-Context
[N/A(0), N/A(0)]
0 0 00000000 00000000 00000000-00000000

		Args: 00000000 00000000 00000000 00000000

[N/A(0), N/A(0)]
0 0 00000000 00000000 00000000-00000000

		Args: 00000000 00000000 00000000 00000000

[IRP_MJ_INTERNAL_DEVICE_CONTROL(f), N/A(c)]
0 e0 ffffcc8301035b50 ffffcc82ff538ca0 fffff800160b4470-ffffcc82ff538ca0 Success Error Cancel
\FileSystem\mydrivername afd!AfdRestartGetAddress
Args: 00000003 00000000 0x0 00000000
[IRP_MJ_DEVICE_CONTROL(e), N/A(b)]
5 0 ffffcc82e51c3cf0 ffffcc8301c17190 00000000-00000000
\Driver\AFD
Args: 0000003c 00000000 0x1202f 00000000
5: kd> !devobj ffffcc8301035b50
Device object (ffffcc8301035b50) is for:
\FileSystem\mydrivername DriverObject ffffcc830bb9e2c0
Current Irp 00000000 RefCount 0 Type 00000012 Flags 00000040
SecurityDescriptor ffff808c0566a6e0 DevExt ffffcc8301035ca0 DevObjExt ffffcc8301035d30
ExtensionFlags (0x00000803) DOE_UNLOAD_PENDING, DOE_DELETE_PENDING,
DOE_DEFAULT_SD_PRESENT
Characteristics (0x00000100) FILE_DEVICE_SECURE_OPEN
Device queue is not busy.
5: kd> !drvobj ffffcc830bb9e2c0
Driver object (ffffcc830bb9e2c0) is for:
\FileSystem\mydrivername

Driver Extension List: (id , addr)

Device Object list:

Sure, the proper way to handle this is to create a normal pnp filter driver and not some hooking abomination.

Windows provides the APIs IoAttachDeviceToDeviceStackSafe and IoDetachDevice, but they do not guarantee safety if the target device is already gone. Is this a bug in Windows, or is it a mistake on my part?

How can my driver wait for all IRPs that may be sent to the device before it is removed?

When you are a PnP filter driver, you'll get the usual IRP_MJ_PNP IRPs that tell you when the device is about to be removed.

When you try to mix PnP and non-PnP drivers, tears usually ensue.

Hi!

Has anyone encountered this issue or have any suggestions on how to handle it properly?
...
Is this a bug in Windows, or is it a mistake on my part?
How can my driver wait for all IRPs that may be sent to the device before it is removed?

In general, it is not safe to detach DO (device object) from stack in arbitrary moment. Other drivers may attach their own DO above yours, someone may hold reference to your DO for further input-output, etc.

By the fact, after calling IoAttachDeviceXxx, you lose control on your DO lifetime...

For PnP stacks, the one "legal" possibility to detach is handling of IRP_MN_REMOVE_DEVICE request. For non-PnP stacks there are no easy and safe ways to achieve this.

Although, you may try this (at your own risk):

FAST_IO_DISPATCH structure (wdm.h)

FastIoDetachDevice

A pointer to a callback routine that is invoked to detach the current device object from a device object that is being deleted.

But, again, you have no choice WHEN to call IoDetachDevice.

Thanks for all the advice. I guess I need to change my approach, my driver needs to be unloaded for updates. It might be a PnP filter driver or a driver that hooks the driver object.