虫趣:FAST_MUTEX死锁

拿到一个dump文件后,经过简单分析,是设备驱动在处理电源IRP时,超时未完成导致的。这是一个典型的0x9F BSOD错误:

DRIVER_POWER_STATE_FAILURE (9f)
A driver has failed to complete a power IRP within a specific time (usually 10 minutes).
Arguments:
Arg1: 0000000000000003, A device object has been blocking an Irp for too long a time
Arg2: fffffa800182aa90, Physical Device Object of the stack
Arg3: fffff800139e9930, nt!TRIAGE_9F_POWER on Win7, otherwise the Functional Device Object of the stack
Arg4: fffffa8002437e10, The blocked IRP

自动分析的内容告诉我:在处理地址为fffffa8002437e10的电源IRP(IRP_MJ_POWER)时,超时(一般10分钟)未能完成,所以引发了系统0x9F蓝屏。自动分析同时列出了当前线程(处理此IRP的线程)调用栈:

Child-SP          RetAddr           Call Site
fffff880`181ab5e0 fffff800`169137cb nt!KiSwapContext(void)+0x76
(Inline Function) --------`-------- nt!KiSwapThread+0xfa
fffff880`181ab720 fffff800`1691260f nt!KiCommitThreadWait+0x23b
fffff880`181ab7e0 fffff800`16977acd nt!KeWaitForSingleObject+0x1cf 
fffff880`181ab870 fffff800`16920255 nt!ExpAcquireFastMutexContended+0x3d
(Inline Function) --------`-------- nt!ExAcquireFastMutex+0x38 
fffff880`181ab8b0 fffff880`046208b6 nt!KeAcquireGuardedMutex(struct _FAST_MUTEX * Mutex = 0xfffffa80`044e81e0)+0x45 
// 中间省略ModuleA的调用
fffff880`181abca0 fffff800`168a2fd9 nt!PopIrpWorker(void * Context = )+0x284
fffff880`181abd50 fffff800`169577e6 nt!PspSystemThreadStartup
fffff880`181abda0 00000000`00000000 nt!KxStartSystemThread(void)+0x16

从调用栈能够看到,线程因为在等待一个同步对象,而进入了休眠状态。从字面上看,这是一个FAST_MUTEX对象。如果能够得到这个对象的地址,就太好了。我试图加载NT模块的私有符号,很幸运成功了。切换到函数KeAcquireGuardedMutex的调用帧后,得到了同步对象的有效信息:

0: kd> dt _fast_mutex 0xfffffa80`044e81e0
nt!_FAST_MUTEX
   +0x000 Count            : 0n4
   +0x008 Owner            : 0xfffffa80`02c8d7c0 Void
   +0x010 Contention       : 8
   +0x018 Event            : _KEVENT
   +0x030 OldIrql          : 0

调试最担心的问题就是地址无效,比较有效的方法是通过关键值的比对,判断地址的有效性。这里我感兴趣的是Owner变量,是这个FAST_MUTEX对象的拥有者线程。如果地址正确的话,我就能够得到一个线程调用栈。切过去看看:

0: kd> !thread 0xfffffa80`02c8d7c0
//省略summary
nt!KiSwapContext+0x76 
nt!KiSwapThread+0xfa (Inline Function @ fffff800`169137cb) 
nt!KiCommitThreadWait+0x23b 
nt!KeWaitForSingleObject+0x1cf 
nt!MiWaitForInPageComplete+0xb8 
nt!MiIssueHardFault+0x1b7 
nt!MmAccessFault+0x81f 
nt!KiPageFault+0x16e (TrapFrame @ fffff880`03b3a810) 
//中间省略ModuleA的调用
nt!PspSystemThreadStartup+0x59 
nt!KxStartSystemThread+0x16

地址是有效的,并且得到的调用栈中也有一堆ModuleA的函数。问题到此可告一段落了:

  1. 线程1在处理IRP_MJ_POWER的时候,试图获取一个FAST_MUTEX同步对象,但因获取失败而进入休眠状态(并因休眠过久,而导致BSOD)。
  2. 在成功获取FAST_MUTEX对象的地址后,从它的结构体中得到了当前拥有此同步对象的线程2地址。
  3. 切换到线程2,发现线程2有一个无法处理的页错误,也同样处于等待状态中。

这个问题到目前为止,可判定是一个死锁问题。只要线程2能够及时释放它所拥有的FAST_MUTEX对象就可以了。但更深层的原因则是线程2竟然因为一个页错误而陷入长时间的等待,得不到解决。由于这个中间的模块是一个保密模块,本人无权限分析。所以到这里为止我的任务就结束了,接下来要让ModuleA模块的部门做进一步分析。

你可能感兴趣的:(windbg,BSOD,0x9f)