万万没想到<Wdf框架之WdfObject状态机(2) >的内容如此之多,2篇博客的篇幅还不够承载,需要第三篇来完成最后一击。本文将进入FxObject::DeleteWorkerAndUnlock的else分支,分析DrvDestroyCallback/DrvCleanupCallback的调用时机。
进入else分支后,首先遇到的函数是FxObject::DisposeChildrenWorker函数,这个函数用来清空所有隶属于本FxObject的子对象,因为kmdfdrv驱动是个空驱动,不存在子对象,所以将跳过两个for循环,直接进入DisposeChildrenWorker尾部的CallCleanup()语句:
kd> kb ;中断在Unload函数,观察句柄drvObj对应的FxObject信息
ChildEBP RetAddr Args to Child
8afabba0 8ad7c2de 5997b8a8 c1763a40 c1763a40 KMDFdrv!Unload+0x4 [g:\kmdfdrv\kmdfdrv\kmdf.c @ 15]
...
8afabbe8 81ecac5f c1763a40 00000000 8f436040 nt!IopLoadUnloadDriver+0x87
kd> !wdfobject 0xa6684750
The type for object 0xa6684750 is FxDriver ;<-------drvObj对应的FxDriver对象的地址
State: FxObjectStateCreated (0x1)
!wdfhandle 0x5997b8a8
dt FxDriver 0xa6684750
...
kd> dt wdf01000!FxDriver 0xa6684750 -y m_ChildListHead ;m_ChildListHead是子对象列表,查看列表中的对象
+0x014 m_ChildListHead : _LIST_ENTRY [ 0xa6684764 - 0xa6684764 ] ;FLink和BLink都指向列表头,所以子对象列表是个空列表
BOOLEAN
FxObject::DisposeChildrenWorker(FxObjectState NewDeferedState,KIRQL OldIrql,BOOLEAN CanDefer)
{
...
if ((m_ObjectFlags & FXOBJECT_FLAGS_DISPOSE_OVERRIDE) == 0x00 || Dispose()) {
CallCleanup();
}
}
CallCleanup调用FxObject::CallCleanupCallbacks,并最终进入KMDFdrv!EvtCleanupCallback函数:
VOID FxObject::CallCleanupCallbacks(VOID)
{
FxContextHeader* pHeader;
WDFOBJECT h;
if (IsCommitted() == FALSE) {
return;
}
h = GetObjectHandle();
for (pHeader = GetContextHeader();
pHeader != NULL;
pHeader = pHeader->NextHeader) {
if (pHeader->EvtCleanupCallback != NULL) {
pHeader->EvtCleanupCallback(h);
pHeader->EvtCleanupCallback = NULL;
}
}
m_ObjectFlags &= ~FXOBJECT_FLAGS_HAS_CLEANUP;
}
FxObject::CallCleanupCallbacks的实现比较简单,因为EvtCleanupCallback存放在FxObject的扩展部分(!wdfobject扩展命令也是把EvtCleanupCallback和EvtDestroyCallback放在FxObject中一起显示),所以代码中首先获得FxObject扩展部分,并调用EvtCleanupCallback,最后把EvtCleanupCallback置空。(FxObject扩展的相关内容以后我会单独分析)。
结合源码,可以发现,调用EvtCleanupCallback时,只有FxObject!m_ObjectState发生改变,至于引用计数,它还没有发生改变,保持进入Unload函数前的值。
kd> kb ;进入EvtCleanupCallback回调时的windbg输出
ChildEBP RetAddr Args to Child
8afabb70 8ad4b9c5 5997b8a8 a6684750 a6684764 KMDFdrv!DrvCleanupCallback [g:\kmdfdrv\kmdfdrv\kmdf.c @ 26]
8afabb84 8ad5119a b8b14bc8 a6684750 c1763a40 Wdf01000!FxObject::CallCleanupCallbacks+0x35 [minkernel\wdf\framework\shared\object\fxobject.cpp @ 354]
8afabba8 8ad7c310 c1763a40 8f436040 8afabbc4 Wdf01000!FxObject::DeleteObject+0x24dba [minkernel\wdf\framework\shared\object\fxobjectstatemachine.cpp @ 124]
8afabbb8 ab0b1324 c1f0c5a8 8afabbe8 820eedfd Wdf01000!FxDriver::Unload+0xa1 [minkernel\wdf\framework\shared\core\fxdriver.cpp @ 179]
8afabbc4 820eedfd c1f0c5a8 89e51540 8f436040 KMDFdrv!FxStubDriverUnload+0x1a [d:\th\minkernel\wdf\framework\kmdf\src\dynamic\stub\stub.cpp @ 159]
8afabbe8 81ecac5f c1763a40 00000000 8f436040 nt!IopLoadUnloadDriver+0x87
kd> ?? drvObj
void * 0x5997b8a8
kd> !wdfhandle 0x5997b8a8
Dumping WDFHANDLE 0x5997b8a8
=============================
Handle type is WDFDRIVER
Refcount: 1 ;<----引用计数值==1
Contexts:
context: dt 0xa6684830 DrvCtx (size is 0xc bytes)
EvtCleanupCallback ab0b10a0 KMDFdrv!DrvCleanupCallback
EvtDestroyCallback ab0b10c0 KMDFdrv!DrvDestroyCallback
!wdfobject 0xa6684750
执行完DisposeChildrenWorker后,旋即进入FxObject::DeletedAndDisposedWorkerLocked函数:
VOID
FxObject::DeletedAndDisposedWorkerLocked(
__in __drv_when(Unlock, __drv_restoresIRQL) KIRQL OldIrql,
__in BOOLEAN Unlock
)
{
SetObjectStateLocked(FxObjectStateDeletedAndDisposed); //设置FxObject状态
if (Unlock) {
m_SpinLock.Release(OldIrql);
}
//对应这个空驱动,FxDriver没有子对象,所以就不跟进这个函数
DestroyChildren();
//敲黑板划重点
RELEASE(NULL);
}
RELEASE(NULL)是段宏,因为source insight中满是Release的引用,从茫茫人海中定位那个它,真的很困难:
//FxObject.hpp
#define RELEASE(_tag) Release(_tag, __LINE__, __FILE__)
virtual ULONG Release(PVOID Tag = NULL,LONG Line = 0,PCSTR File = NULL)
{
ULONG c;
c = InterlockedDecrement(&m_Refcnt);
if (c == 0) {
FinalRelease();
}
return c;
}
执行RELEASE时,会减少引用计数,如果引用计数归零,则调用FinalRelease,由它调用EvtDestroyCallback函数。之后,FxDriver对象的内存被框架释放,不宜再被使用。
VOID FxObject::ProcessDestroy(VOID)
{
//...
for (pHeader = GetContextHeader();
pHeader != NULL;
pHeader = pHeader->NextHeader) {
if (pHeader->EvtCleanupCallback != NULL) {
pHeader->EvtCleanupCallback(h);
pHeader->EvtCleanupCallback = NULL;
}
if (pHeader->EvtDestroyCallback != NULL) {
pHeader->EvtDestroyCallback(h);
pHeader->EvtDestroyCallback = NULL;
}
}
...
//
SelfDestruct(); //SelfDestruct是delete this的封装
}
kd> kb
ChildEBP RetAddr Args to Child
8afabb80 8ad51245 5997b8a8 b8b14bc8 a6684750 KMDFdrv!DrvDestroyCallback [g:\kmdfdrv\kmdfdrv\kmdf.c @ 20]
8afabba8 8ad7c310 c1763a40 8f436040 8afabbc4 Wdf01000!FxObject::DeleteObject+0x24e65 [minkernel\wdf\framework\shared\object\fxobjectstatemachine.cpp @ 124]
8afabbb8 ab0b1324 c1f0c5a8 8afabbe8 820eedfd Wdf01000!FxDriver::Unload+0xa1 [minkernel\wdf\framework\shared\core\fxdriver.cpp @ 179]
8afabbc4 820eedfd c1f0c5a8 89e51540 8f436040 KMDFdrv!FxStubDriverUnload+0x1a [d:\th\minkernel\wdf\framework\kmdf\src\dynamic\stub\stub.cpp @ 159]
8afabbe8 81ecac5f c1763a40 00000000 8f436040 nt!IopLoadUnloadDriver+0x87
kd> !wdfhandle 0x5997b8a8
Dumping WDFHANDLE 0x5997b8a8
=============================
Handle type is WDFDRIVER
Refcount: 0 ;引用计数值==0
!wdfobject 0xa6684750
kd> !wdfobject 0xa6684750
State: FxObjectStateDeletedAndDisposed (0xa) ;FxObject状态
在EvtDestroyCallback中,引用计数已经为0,这种情况下再减少引用计数会造成数据溢出,所以很多书在介绍EvtCleanupCallback时会提到减少FxObject引用计数,却从没见过有哪本书说在EvtDestroyCallback中减少引用计数。虽然两者都可以做一些资源释放相关的操作。
标签:pHeader,__,Wdf,FxObject,c1763a40,状态机,NULL,WdfObject,EvtCleanupCallback From: https://blog.51cto.com/u_13927568/5830870