'TmpIsClusteredTransactionManager'에 해당되는 글 1건

  1. 2011.09.26 [윈도우 NT] nt!TmpIsClusteredTransactionManager 버그 2
Windows OS2011. 9. 26. 20:58
반응형
드라이버가 언로드되지 않는 현상이 발생해 원인을 찾아보다가 발견한 OS 버그다.
Vista와 Windows 7 커널에 존재하는 버그고 확인해 보니 Windows 8 커널에서는 수정되었다.

어떤 볼륨이 NTFS로 포맷되어 있는 경우 마운트되면서 커널이 볼륨 디바이스의 레퍼런스 카운트를 하나 올리고 내리지 않아서 발생하는 문제다. 레퍼런스 카운트가 남아 있으므로 드라이버가 내려가지 않는다. -_-;;;

NTFS 파일시스템 관련 초기화를 하는 중에 TransactionManager를 생성하는데 여기에 버그가 존재한다.
(Vista부터 Transactional NTFS(Txf)가 도입되었는데 이와 관련된 초기화 작업으로 보인다.)

문제의 원인이 되는 콜스택은 아래와 같다.
마운트가 완료된 직후 볼륨을 초기화하는 과정이다. 


nt!ObfReferenceObject+0x24 (FPO: [0,0,0])
nt!IoGetAttachedDeviceReference+0x23
nt!TmpIsClusteredTransactionManager+0x29  ; IoGetAttachedDeviceReference()를 
                                                                      ; 호출하고 dereference하지 않는 버그가 있다
nt!TmpCreateLogFile+0x18d
nt!TmpCreateOrOpenLogTransactionManager+0x34
nt!TmInitializeTransactionManager+0x1e1
nt!NtCreateTransactionManager+0x155
nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 8831b9e8)
nt!ZwCreateTransactionManager+0x11 (FPO: [6,0,0])
Ntfs!TxfCreateTmInstance+0x9d (FPO: [Non-Fpo])
Ntfs!TxfStartRm+0xceb (FPO: [Non-Fpo])
Ntfs!TxfInitializeVolume+0x688 (FPO: [Non-Fpo])
Ntfs!NtfsCommonFileSystemControl+0x99 (FPO: [Non-Fpo])
Ntfs!NtfsFspDispatch+0x264 (FPO: [Non-Fpo])
nt!ExpWorkerThread+0xfd
nt!PspSystemThreadStartup+0x9d
nt!KiThreadStartup+0x16



nt!TmpIsClusteredTransactionManager 함수의 내용을 들여다 보면 IoGetAttachedDeviceReference를 호출하고 나서 ObDereferenceObject를 호출하지 않는 것을 확인할 수 있다.

kd> uf nt!TmpIsClusteredTransactionManager

nt!TmpIsClusteredTransactionManager:

82d9e5d3 8bff            mov     edi,edi

82d9e5d5 55              push    ebp

82d9e5d6 8bec            mov     ebp,esp

82d9e5d8 83ec20          sub     esp,20h


...


82d9e5f1 8b4508          mov     eax,dword ptr [ebp+8]

82d9e5f4 ff7004          push    dword ptr [eax+4]

82d9e5f7 e8bac9f1ff      call    nt!IoGetAttachedDeviceReference (82cbafb6)


...


nt!TmpIsClusteredTransactionManager+0x5d:

82d9e630 8bd0            mov     edx,eax

82d9e632 8bce            mov     ecx,esi

82d9e634 e81fdee9ff      call    nt!IofCallDriver (82c3c458)


...


nt!TmpIsClusteredTransactionManager+0xbd:

82d9e690 5e              pop     esi

82d9e691 5b              pop     ebx

82d9e692 c9              leave

82d9e693 c20800          ret     8


함수가 끝나도 ObDereferenceObject를 호출하는 부분은 없다.

이 함수가 어떻게 볼륨 디바이스에 영향을 미치는지 이해하기 위해 볼륨 디바이스 스택을 확인해 본다.
일반적인 볼륨 디바이스 스택은 아래와 같다. 


kd> !devstack 8514d400
  !DevObj   !DrvObj            !DevExt   ObjectName
  852c6020  \Driver\volsnap    852c60d8  
  852bf020  \Driver\Ecache     852bf0d8  
> 8514d400  \Driver\volmgr     8514d4b8  HarddiskVolume1


레퍼런스 카운트가 올라간 디바이스 오브젝트는 HarddiskVolume1의 854d400일까?
아니다.

HarddiskVolume1에 대한 마운트가 일어나면서 이 녀석에 대해 Ntfs!TxfInitializeVolume가 발생하지만 nt!TmpIsClusteredTransactionManager가  IoGetAttachedDeviceReference를 호출하기 때문에 최상위 스택의 852c6020의 레퍼런스 카운트가 올라간다.

따라서 HarddiskVolume1 디바이스 오브젝트는 잘 사라지고 \Driver\volmgr도 잘 언로드 될 수 있다.
하지만 디바이스 오브젝트 852c6020은 레퍼런스 카운트가 1 남기 때문에 Delete해도 DELETE_PENDING으로 남아 있고 \Driver\volsnap도 영원히 내려가지 않는다.

OS에서 volsnap은 원래 내리지 않기 때문에 별다른 문제없이 잘 사용하고 있었나 보다.

혹시 자기만의 볼륨 디바이스를 만들다가 드라이버가 내려가지 않는 현상이 발생한다면...
nt!TmpIsClusteredTransactionManager에 의한 레퍼런스 카운트 문제가 아닌지 확인해 봐야 한다.

반응형
Posted by GreeMate