天天看點

Wdf架構之WdfObject狀态機(2) 一文再補充

    萬萬沒想到<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);
}      
//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;
}      
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狀态