I'm currently trying to debug a system deadlock and I'm having a hard time understanding this.
Child-SP RetAddr : Args to Child : Call Site
fffff880`035cb760 fffff800`02ecef72 : 00000000`00000002 fffffa80`066e8b50 00000000`00000000 fffffa80`066a16e0 : nt!KiSwapContext+0x7a
fffff880`035cb8a0 fffff800`02ee039f : fffffa80`0b9256b0 00000000`000007ff 00000000`00000000 00000000`00000000 : nt!KiCommitThreadWait+0x1d2
fffff880`035cb930 fffff880`0312a5e4 : 00000000`00000000 fffff800`00000000 fffffa80`079a3c00 00000000`00000000 : nt!KeWaitForSingleObject+0x19
Why would the first argument for KeWaitForSingleObject be null?
Unless I'm misunderstanding isn't the first argument the object being waited on? Is the deadlock simply that this thread is waiting on nothing or is this ordinary behavior?
Additionally I see another process (services.exe) showing a similar stack trace:
1: kd> .thread fffffa800d406b50
Implicit thread is now fffffa80`0d406b50
1: kd> kv
*** Stack trace for last set context - .thread/.cxr resets it
Child-SP RetAddr : Args to Child : Call Site
fffff880`09ed4800 fffff800`02ecef72 : fffffa80`0d406b50 fffffa80`0d406b50 00000000`00000000 fffff8a0`00000000 : nt!KiSwapContext+0x7a
fffff880`09ed4940 fffff800`02ee039f : 00000000`000000b4 fffffa80`0b1df7f0 00000000`0000005e fffff800`031ae5e7 : nt!KiCommitThreadWait+0x1d2
fffff880`09ed49d0 fffff800`031d1e3e : fffffa80`0d406b00 00000000`00000006 00000000`00000001 00000000`093bf000 : nt!KeWaitForSingleObject+0x19f
fffff880`09ed4a70 fffff800`02ed87d3 : fffffa80`0d406b50 00000000`77502410 fffff880`09ed4ab8 fffffa80`0b171a50 : nt!NtWaitForSingleObject+0xde
Is this thread waiting on itself essentially?
You're debugging a 64-bit process.
Remember the x64 calling convention, which is explained here. The first 4 arguments are passed in registers. After that, arguments are pushed onto the stack.
Unfortunately, kv
blindly displays the stack arguments. In fact, it would be quite difficult (and sometimes impossible) for it to determine what the first 4 arguments actually were at the time of the call since they may not have been stored anywhere that can ever be recovered.
So, you are looking at the 5th argument to nt!NtWaitForSingleObject
, where a nullptr
is a pretty typical argument for a Timeout
.
Luckily for us debugging types, all is not lost! There is a windbg extension which does its best to reconstruct the arguments when the function was called. The extension is called CMKD. You can place the extension DLL in your winext
folder and call it like so:
0:000> !cmkd.stack -p
Call Stack : 7 frames
## Stack-Pointer Return-Address Call-Site
00 000000a408c7fb28 00007ffda95b1148 ntdll!NtWaitForSingleObject+a
Parameter[0] = 0000000000000034
Parameter[1] = 0000000000000000
Parameter[2] = 0000000000000000
Parameter[3] = (unknown)
01 000000a408c7fb30 00007ff7e44c13f1 KERNELBASE!WaitForSingleObjectEx+98
Parameter[0] = 0000000000000034
Parameter[1] = 00000000ffffffff
Parameter[2] = 0000000000000000
Parameter[3] = 00007ff7e44cba28
02 000000a408c7fbd0 00007ff7e44c3fed ConsoleApplication2!main+41
Parameter[0] = (unknown)
Parameter[1] = (unknown)
Parameter[2] = (unknown)
Parameter[3] = (unknown)
Notice that it does not always succeed at finding the argument, as some of them are (unknown)
. But, it does a pretty good job and can be an invaluable tool when debugging 64-bit code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With